diff options
Diffstat (limited to 'docs/source/pages/tutorial.rst')
-rw-r--r-- | docs/source/pages/tutorial.rst | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/docs/source/pages/tutorial.rst b/docs/source/pages/tutorial.rst new file mode 100644 index 0000000..6db7f6c --- /dev/null +++ b/docs/source/pages/tutorial.rst @@ -0,0 +1,207 @@ +.. _tutorial: + +Tutorial +======== + +In order to help you with using *pygls* in VSCode, we have created the `vscode-playground`_ extension. + +.. note:: + + This extension is meant to provide an environment in which you can easily experiment with a *pygls* powered language server. + It is not necessary in order to use *pygls* with other text editors. + + If you decide you want to publish your language server on the VSCode marketplace this + `template extension <https://github.com/microsoft/vscode-python-tools-extension-template>`__ + from Microsoft a useful starting point. + +Prerequisites +------------- + +In order to setup and run the example VSCode extension, you need following software +installed: + +* `Visual Studio Code <https://code.visualstudio.com/>`_ editor +* `Python 3.8+ <https://www.python.org/downloads/>`_ +* `vscode-python <https://marketplace.visualstudio.com/items?itemName=ms-python.python>`_ extension +* A clone of the `pygls <https://github.com/openlawlibrary/pygls>`_ repository + +.. note:: + If you have created virtual environment, make sure that you have *pygls* installed + and `selected appropriate python interpreter <https://code.visualstudio.com/docs/python/environments>`_ + for the *pygls* project. + + +Running the Example +------------------- + +For a step-by-step guide on how to setup and run the example follow `README`_. + +Hacking the Extension +--------------------- + +When you have successfully setup and run the extension, open `server.py`_ and +go through the code. + +We have implemented following capabilities: + +- ``textDocument/completion`` feature +- ``countDownBlocking`` command +- ``countDownNonBlocking`` command +- ``textDocument/didChange`` feature +- ``textDocument/didClose`` feature +- ``textDocument/didOpen`` feature +- ``showConfigurationAsync`` command +- ``showConfigurationCallback`` command +- ``showConfigurationThread`` command + +When running the extension in *debug* mode, you can set breakpoints to see +when each of above mentioned actions gets triggered. + +Visual Studio Code supports *Language Server Protocol*, which means, that every +action on the client-side, will result in sending request or notification to +the server via JSON RPC. + +Debug Code Completions +~~~~~~~~~~~~~~~~~~~~~~ + +Set a breakpoint inside ``completion`` function and go back to opened *json* +file in your editor. Now press ``ctrl + space`` (``control + space`` on mac) to +show completion list and you will hit the breakpoint. When you continue +debugging, the completion list pop-up won't show up because it was closing when +the editor lost focus. + +Similarly, you can debug any feature or command. + +Keep the breakpoint and continue to the next section. + +Blocking Command Test +~~~~~~~~~~~~~~~~~~~~~ + +In order to demonstrate you that blocking the language server will reject other +requests, we have registered a custom command which counts down 10 seconds and +sends notification messages to the client. + +1. Press **F1**, find and run ``Count down 10 seconds [Blocking]`` command. +2. Try to show *code completions* while counter is still ticking. + +Language server is **blocked**, because ``time.sleep`` is a +**blocking** operation. This is why you didn't hit the breakpoint this time. + +.. hint:: + + To make this command **non blocking**, add ``@json_server.thread()`` + decorator, like in code below: + + .. code-block:: python + + @json_server.thread() + @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_BLOCKING) + def count_down_10_seconds_blocking(ls, *args): + # Omitted + + *pygls* uses a **thread pool** to execute functions that are marked with + a ``thread`` decorator. + + +Non-Blocking Command Test +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Python 3.4 introduced *asyncio* module which allows us to use asynchronous +functions (aka *coroutines*) and do `cooperative multitasking`_. Using the +`await` keyword inside your coroutine will give back control to the +scheduler and won't block the main thread. + +1. Press **F1** and run the ``Count down 10 seconds [Non Blocking]`` command. +2. Try to show *code completions* while counter is still ticking. + +Bingo! We hit the breakpoint! What just happened? + +The language server was **not blocked** because we used ``asyncio.sleep`` this +time. The language server was executing *just* in the *main* thread. + + +Text Document Operations +~~~~~~~~~~~~~~~~~~~~~~~~ + +Opening and closing a JSON file will display appropriate notification message +in the bottom right corner of the window and the file content will be +validated. Validation will be performed on content changes, as well. + +Show Configuration Data +~~~~~~~~~~~~~~~~~~~~~~~ + +There are *three* ways for getting configuration section from the client +settings. + +.. note:: + + *pygls*' built-in coroutines are suffixed with *async* word, which means that + you have to use the *await* keyword in order to get the result (instead of + *asyncio.Future* object). + +- **Get the configuration inside a coroutine** + +.. code-block:: python + + config = await ls.get_configuration_async(ConfigurationParams([ + ConfigurationItem('', JsonLanguageServer.CONFIGURATION_SECTION) + ])) + +- **Get the configuration inside a normal function** + +We already saw that we *don't* want to block the main thread. Sending the +configuration request to the client will result with the response from it, but +we don't know when. You have to pass *callback* function which will be +triggered once response from the client is received. + +.. code-block:: python + + def _config_callback(config): + try: + example_config = config[0].exampleConfiguration + + ls.show_message( + f'jsonServer.exampleConfiguration value: {example_config}' + ) + + except Exception as e: + ls.show_message_log(f'Error ocurred: {e}') + + ls.get_configuration(ConfigurationParams([ + ConfigurationItem('', JsonLanguageServer.CONFIGURATION_SECTION) + ]), _config_callback) + +As you can see, the above code is hard to read. + +- **Get the configuration inside a threaded function** + +Blocking operations such as ``future.result(1)`` should not be used inside +normal functions, but to increase the code readability, you can add the +*thread* decorator to your function to use *pygls*' *thread pool*. + +.. code-block:: python + + @json_server.thread() + @json_server.command(JsonLanguageServer.CMD_SHOW_CONFIGURATION_THREAD) + def show_configuration_thread(ls: JsonLanguageServer, *args): + """Gets exampleConfiguration from the client settings using a thread pool.""" + try: + config = ls.get_configuration(ConfigurationParams([ + ConfigurationItem('', JsonLanguageServer.CONFIGURATION_SECTION) + ])).result(2) + + # ... + +This way you won't block the main thread. *pygls* will start a new thread when +executing the function. + +Modify the Example +~~~~~~~~~~~~~~~~~~ + +We encourage you to continue to :ref:`user guide <user-guide>` and +modify this example. + +.. _vscode-playground: https://github.com/openlawlibrary/pygls/blob/main/examples/vscode-playground +.. _README: https://github.com/openlawlibrary/pygls/blob/main/examples/vscode-playground/README.md +.. _server.py: https://github.com/openlawlibrary/pygls/blob/main/examples/servers/json_server.py +.. _cooperative multitasking: https://en.wikipedia.org/wiki/Cooperative_multitasking |