pax_global_header00006660000000000000000000000064145557016620014525gustar00rootroot0000000000000052 comment=905734118190ad04c9bae1a1120b91facaf29dc4 pygls-1.3.0/000077500000000000000000000000001455570166200126645ustar00rootroot00000000000000pygls-1.3.0/.editorconfig000066400000000000000000000002501455570166200153360ustar00rootroot00000000000000root = true [*] charset = utf-8 indent_style = space indent_size = 4 insert_final_newline = true end_of_line = lf [*.{yml,yaml}] indent_style = space indent_size = 2 pygls-1.3.0/.git-blame-ignore-revs000066400000000000000000000017741455570166200167750ustar00rootroot00000000000000# This file contains a list of commits that are not likely what you # are looking for in a blame, such as mass reformatting or renaming. # You can set this file as a default ignore file for blame by running # the following command. # # $ git config blame.ignoreRevsFile .git-blame-ignore-revs # # To temporarily not use this file add # --ignore-revs-file="" # to your blame command. # # The ignoreRevsFile can't be set globally due to blame failing if the file isn't present. # To not have to set the option in every repository it is needed in, # save the following script in your path with the name "git-bblame" # now you can run # $ git bblame $FILE # to use the .git-blame-ignore-revs file if it is present. # # #!/usr/bin/env bash # repo_root=$(git rev-parse --show-toplevel) # if [[ -e $repo_root/.git-blame-ignore-revs ]]; then # git blame --ignore-revs-file="$repo_root/.git-blame-ignore-revs" $@ # else # git blame $@ # fi # chore: introduce `black` formatting 86b36e271ebde5ac4f30bf83c4e7ee42ba5af9ac pygls-1.3.0/.gitattributes000066400000000000000000000012471455570166200155630ustar00rootroot00000000000000# These settings are for any web project # Handle line endings automatically for files detected as text # and leave all files detected as binary untouched. * text=auto # Force the following filetypes to have unix eols, so Windows does not break them *.* text eol=lf # ## These files are binary and should be left untouched # # (binary is a macro for -text -diff) *.png binary *.jpg binary *.jpeg binary *.gif binary *.ico binary *.mov binary *.mp4 binary *.mp3 binary *.flv binary *.fla binary *.swf binary *.gz binary *.zip binary *.7z binary *.ttf binary *.eot binary *.woff binary *.pyc binary *.pdf binary *.ez binary *.bz2 binary *.swp binary *.whl binary *.docx binary pygls-1.3.0/.github/000077500000000000000000000000001455570166200142245ustar00rootroot00000000000000pygls-1.3.0/.github/FUNDING.yml000066400000000000000000000001061455570166200160360ustar00rootroot00000000000000# These are supported funding model platforms github: OpenLawLibrary pygls-1.3.0/.github/workflows/000077500000000000000000000000001455570166200162615ustar00rootroot00000000000000pygls-1.3.0/.github/workflows/ci.yml000066400000000000000000000103341455570166200174000ustar00rootroot00000000000000name: CI on: push: branches: - main pull_request: branches: - main defaults: run: shell: bash jobs: pre_job: runs-on: ubuntu-latest outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: concurrent_skipping: 'outdated_runs' cancel_others: 'true' skip_after_successful_duplicate: 'false' test: needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' strategy: matrix: os: [ubuntu-latest, windows-latest] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - name: Use Python ${{ matrix.python-version }} uses: actions/setup-python@v4 id: setup-python with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Install Poetry uses: snok/install-poetry@v1 with: version: '1.5.1' virtualenvs-in-project: true - name: Load cached venv id: cached-poetry-dependencies uses: actions/cache@v3 with: path: .venv key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: poetry install --all-extras - name: Run tests run: | source $VENV # Only needed because of Github Action caching poe test test-pyodide: needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Use Python "3.10" uses: actions/setup-python@v4 with: python-version: "3.10" - name: Install Poetry uses: snok/install-poetry@v1 with: virtualenvs-in-project: true - name: Install Dependencies if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: | poetry install --with pyodide - name: Run Testsuite uses: nick-fields/retry@v2 with: timeout_minutes: 10 max_attempts: 6 command: | source $VENV poe test-pyodide || true lint: needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Use Python uses: actions/setup-python@v4 with: python-version: "3.x" - name: Install Poetry uses: snok/install-poetry@v1 with: virtualenvs-in-project: true - name: Load cached venv id: cached-poetry-dependencies uses: actions/cache@v3 with: path: .venv key: venv-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: poetry install --all-extras --with dev - name: Run lints run: | source $VENV poe lint build: needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Use Python uses: actions/setup-python@v4 with: python-version: "3.x" - name: Install Poetry uses: snok/install-poetry@v1 with: virtualenvs-in-project: true - name: Load cached venv id: cached-poetry-dependencies uses: actions/cache@v3 with: path: .venv key: venv-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: poetry install --all-extras - name: Build packages (sdist and wheel) run: | git describe --tags --abbrev=0 poetry build - name: Upload builds uses: actions/upload-artifact@v3 with: name: build-artifacts path: "dist/*" pygls-1.3.0/.github/workflows/json-extension.yml000066400000000000000000000035101455570166200217660ustar00rootroot00000000000000name: vscode-playground on: - push - pull_request jobs: build: runs-on: ubuntu-latest defaults: run: shell: bash working-directory: examples/vscode-playground steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: python-version: "3.x" - uses: actions/setup-node@v2 with: node-version: "16" - uses: actions/cache@v2 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('examples/vscode-playground/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- - name: Install dependencies run: | npm i npm i vsce - name: Lint run: npx eslint src/*.ts - name: Compile run: npm run compile - name: Replace package.json version run: | replace_packagejson_version() { version_line=$(grep -o '"version".*' $1) version=$(python -m json.tool package.json | awk -F'"' '/version/{print $4}') build_version=$version+$2 build_version_line=${version_line/$version/$build_version} sed -i "s|$version_line|$build_version_line|g" $1 cat $1 } replace_packagejson_version package.json $GITHUB_RUN_ID - name: Build VSIX run: npx vsce package - name: Validate VSIX run: | npx vsce ls | grep package.json npx vsce ls | grep out/extension.js - name: Upload VSIX uses: actions/upload-artifact@v2 with: name: vscode-playground-vsix # The path must be rooted from the directory GitHub Actions starts # from, not the working-directory. path: examples/vscode-playground/*.vsix if-no-files-found: error pygls-1.3.0/.github/workflows/release.yml000066400000000000000000000031231455570166200204230ustar00rootroot00000000000000name: Release Pygls to PyPI on: release: types: [published] jobs: relase: name: "🚀 Release 🚢" runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: ssh-key: ${{secrets.CI_RELEASE_DEPLOY_KEY}} fetch-depth: 0 - name: Use Python "3.10" uses: actions/setup-python@v4 with: python-version: "3.10" - name: Install Poetry uses: snok/install-poetry@v1 - name: Generate the latest changelog uses: orhun/git-cliff-action@v2 id: git-cliff with: config: cliff.toml args: --verbose --latest env: OUTPUT: git-cliff-changes.tmp.md - name: Update the changelog run: | git checkout main cat git-cliff-changes.tmp.md | sed -i "3r /dev/stdin" CHANGELOG.md git config --global user.name 'Github Action' git config --global user.email 'github.action@users.noreply.github.com' git add CHANGELOG.md git commit -m "chore: update CHANGELOG.md" git push - name: Update CONTRIBUTORS.md run: | git checkout main poetry install poetry run poe generate_contributors_md if [[ $(git diff --stat CONTRIBUTORS.md) != '' ]]; then git add CONTRIBUTORS.md git commit -m "chore: update CONTRIBUTORS.md" git push fi - name: Release run: | poetry build poetry publish --username "__token__" --password ${{ secrets.PYPI_API_TOKEN }} pygls-1.3.0/.gitignore000066400000000000000000000025051455570166200146560ustar00rootroot00000000000000.idea .dir-locals.el *.vsix # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* cov.pth .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # OS files .DS_Store # mypy .mypy_cache .dmypy.json /pyodide_testrunner # VS Code settings .vscode/ !examples/servers/.vscode pygls-1.3.0/.readthedocs.yaml000066400000000000000000000012031455570166200161070ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the OS, Python version and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the "docs/" directory with Sphinx sphinx: configuration: docs/source/conf.py # Optional but recommended, declare the Python requirements required # to build your documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt - method: pip path: . pygls-1.3.0/CHANGELOG.md000066400000000000000000000443331455570166200145040ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. ## [1.2.1] - 2023-11-30 More details: https://github.com/openlawlibrary/pygls/releases/tag/v1.2.1 ### Bug Fixes - Handle ResponseErrors correctly ### Miscellaneous Tasks - Update CHANGELOG.md - Clean CHANGELOG ### Build - V1.2.1 ## [1.2.0] - 2023-11-18 More details: https://github.com/openlawlibrary/pygls/releases/tag/v1.2.0 ### Bug Fixes - Remove dependency on typeguard - Linting and formatting issues - Simplify option validation check - Index error on empty workspace ### Features - Allow user to override Python interpreter ### Miscellaneous Tasks - Update CHANGELOG.md - Update CONTRIBUTORS.md - Update `poetry.lock` after removing typeguard - Add example configuration - Pin lsprotocol to 2023.0.0 ### Refactor - Move workspace/ into servers/ dir ## [1.1.2] - 2023-10-28 More details: https://github.com/openlawlibrary/pygls/releases/tag/v1.1.2 ### Documentation - Correct doc comment for PositionCodec.client_num_units ### Miscellaneous Tasks - Update CHANGELOG.md - Update CONTRIBUTORS.md - Split protocol.py into own folder/files ### Build - Bump urllib3 from 2.0.6 to 2.0.7 - Allow installation with typeguard 4.x - V1.1.2 ## [1.1.1] - 2023-10-06 More details: https://github.com/openlawlibrary/pygls/releases/tag/v1.1.1 ### Bug Fixes - Prevent AttributeError root_path when no workspace ### CI - Fix release process ### Miscellaneous Tasks - Manual changes for v1.1.0 release - Explicit exports from pygls.workspace ### Build - Bump urllib3 from 2.0.5 to 2.0.6 - V1.1.1 ## [1.1.0] - 2023-10-02 More details: https://github.com/openlawlibrary/pygls/releases/tag/v1.1.0 ### Bug Fixes - Fix broken link and outdated comment - Correctly cast from UTF16 positions - Ensure server commands can be executed - Mypy lints - Error code of JsonRpcInternalError - Only show code action when there's no sum - Don't include trailing whitespace in code action title - 'bool' object has no attribute 'resolve_provider' - Computation of formatting and diagnostic provider capabilities ### CI - Migrate to Poetry and modernise - Linter for conventional commits - Autogenerate changelog with `git-cliff` - Automate CONTRIBUTORS.md - Retry Pyodide tests - Test against Python 3.12 - Use `matrix.python-version` in cache key - Update json-extension pipeline - Pin poetry to 1.5.1 - Do not install chromium/chromedriver - Enable coverage reporting - Run all lints even when some fail - Increase Pyodide CI retries to 6 ### Documentation - Use autodoc to document client methods - Update docstrings - Change specification for commit messages - Typo in vscode-playground README.md - Add api docs for servers, protocol and workspace - Align docstring formatting - Handle methods starting with `$/` - Update links and code snippets - Rename advanced usage to user guide - Instructions for using plain text files with vscode-playground ### Features - Add document diagnostic capability - Add workspace symbol resolve - Add workspace diagnostic support - Adds inline value support - Adds type hierarchy request support - Add `await` syntax support for sending edit request to client - Allow servers to provide `NotebookDocumentSyncOptions` - Initial support for notebook document syncronisation - Add notebook support to example `inlay_hints.py` server - Accept `PositionEncoding` client capability - Support UTF32 ans UTF8 position encoding ### Miscellaneous Tasks - Update autogenerated Pygls client - Introduce `black` formatting - Add `.git-blame-ignore-revs` file - Delete fountain-vscode-extension - Update README.md - Bump lsprotocol version - Fix deprecation warning, set chrome path - Disable body-max-line-length check - Add .readthedocs.yaml - Strict types in uris.py and workspace.py - Move workspace/doc/position into own files - Fix mypy types - Maintain `Workspace` backwards compat - Fix use of deprecated methods in tests/test_language_server.py ### Refactor - Move example json-server to `examples/servers` - Rename `json-vscode-extension/` -> `vscode-playground` - Simplify end-to-end test client fixture definition - Rename `Client` -> `JsonRPCClient` - Rename `LanguageClient` -> `BaseLanguageClient` - Rename `_document` to `_text_document` - Expose workspace via a property - Server `Position` class - Rename server Position to PositionCodec, instantiate it in Workspace - Reference types via `types` module - Make `default` argument mandatory, add type annotations ### Testing - Test that the client provided token is used - Remove a useless sleep - Test cases of server initiated progress - Base Pyodide wheel deps off poetry.lock ### Build - Bump semver in /examples/fountain-vscode-extension - Bump semver in /examples/json-vscode-extension - Bump word-wrap in /examples/json-vscode-extension - Lock min Python version to 3.7.9 - Cache specific Python minor version - Bump lsprotocol to 2023.0.0b1 - Release v1.1.0 ### Json-extension - Support cancellation in progress example ### Progress - Support work done progress cancellation from client ### Server - Add a type annotation to help completions in editor ### Extra Notes #### Added - Add `LanguageClient` with LSP methods autogenerated from type annotations in `lsprotocol` ([#328]) - Add base JSON-RPC `Client` with support for running servers in a subprocess and communicating over stdio. ([#328]) - Support work done progress cancel ([#253]) - Add support for `textDocument/inlayHint` and `inlayHint/resolve` requests ([#342]) #### Changed #### Fixed - `pygls` no longer overrides the event loop for the current thread when given an explicit loop to use. ([#334]) - Fixed `MethodTypeNotRegisteredError` when registering a `TEXT_DOCUMENT_DID_SAVE` feature with options. ([#338]) - Fixed detection of `LanguageServer` type annotations when using string-based annotations. ([#352]) [#328]: https://github.com/openlawlibrary/pygls/issues/328 [#334]: https://github.com/openlawlibrary/pygls/issues/334 [#338]: https://github.com/openlawlibrary/pygls/discussions/338 [#253]: https://github.com/openlawlibrary/pygls/pull/253 [#342]: https://github.com/openlawlibrary/pygls/pull/342 [#304]: https://github.com/openlawlibrary/pygls/issues/304 # Pre Automation Changelog ## [1.0.2] - May 15th, 2023 ### Changed - Update typeguard to 3.x ([#327]) [#327]: https://github.com/openlawlibrary/pygls/issues/327 ### Fixed - Data files are no longer placed inside the wrong `site-packages` folder when installing `pygls` ([#232]) [#232]: https://github.com/openlawlibrary/pygls/issues/232 ## [1.0.1] - February 16th, 2023 ### Fixed - Fix progress example in json extension. ([#230]) - Fix `AttributeErrors` in `get_configuration_async`, `get_configuration_callback`, `get_configuration_threaded` commands in json extension. ([#307]) - Fix type annotations for `get_configuration_async` and `get_configuration` methods on `LanguageServer` and `LanguageServerProtocol` objects ([#307]) - Provide `version` param for publishing diagnostics ([#303]) - Relaxed the Python version upper bound to `<4` ([#318]) [#230]: https://github.com/openlawlibrary/pygls/issues/230 [#303]: https://github.com/openlawlibrary/pygls/issues/303 [#307]: https://github.com/openlawlibrary/pygls/issues/307 [#318]: https://github.com/openlawlibrary/pygls/issues/318 ## [1.0.0] - 2/12/2022 ### Changed BREAKING CHANGE: Replaced `pydantic` with [`lsprotocol`](https://github.com/microsoft/lsprotocol) ## [0.13.1] - 1/12/2022 ### Changed Docs now state that the v1 alpha branch is the recommended way to start new projects ### Fixed Support `CodeActionKind.SourceFixAll` ## [0.13.0] - 2/11/2022 ### Added - Add `name` and `version` arguments to the constructor of `LanguageServer` ([#274]) ### Changed - Default behaviour change: uncaught errors are now sent as `showMessage` errors to client. Overrideable in `LanguageServer.report_server_error()`: https://github.com/openlawlibrary/pygls/pull/282 ### Fixed - `_data_recevied()` JSONRPC message parsing errors now caught - Fix "Task attached to a different loop" error in `Server.start_ws` ([#268]) [#274]: https://github.com/openlawlibrary/pygls/issues/274 [#268]: https://github.com/openlawlibrary/pygls/issues/268 ## [0.12.4] - 24/10/2022 ### Fixed - Remove upper bound on Pydantic when Python is <3.11 ## [0.12.3] - 24/10/2022 ### Fixed - Require Pydantic 1.10.2 when Python is 3.11 ## [0.12.2] - 26/09/2022 ### Fixed - Relaxed the Python version upper bound to `<4` ([#266]) [#266]: https://github.com/openlawlibrary/pygls/pulls/266 ## [0.12.1] - 01/08/2022 ### Changed - `Document` objects now expose a text document's `language_id` - Various Pyodide improvements - Improved tests for more reliable CI ## [0.12] - 04/07/2022 ### Added - Allow custom word matching for `Document.word_at_point` ### Changed - Upgraded Python support to 3.10, dropping support for 3.6 - Dependency updates, notably Pydantic 1.9 and Websockets 10 ### Fixed ## [0.11.3] - 11/06/2021 ### Added ### Changed - Update json-example to include an example semantic tokens method ([#204]) ### Fixed - Fix example extension client not detecting debug mode appropriately ([#193]) - Fix how the `semantic_tokens_provider` field of `ServerCapabilities` is computed ([#213]) [#193]: https://github.com/openlawlibrary/pygls/issues/193 [#204]: https://github.com/openlawlibrary/pygls/issues/204 [#213]: https://github.com/openlawlibrary/pygls/pulls/213 ## [0.11.2] - 07/23/2021 ### Added ### Changed ### Fixed - Fix feature manager ([#203]) - Use `127.0.0.1` for tests and examples to avoid Docker issues ([#165]) [#203]: https://github.com/openlawlibrary/pygls/issues/203 [#165]: https://github.com/openlawlibrary/pygls/issues/165 ## [0.11.1] - 06/21/2021 ### Added ### Changed - Remove defaults from all optional fields on protocol-defined types ([#198]) ### Fixed [#198]: https://github.com/openlawlibrary/pygls/pull/198 ## [0.11.0] - 06/18/2021 ### Added - Testing against Python 3.9 ([#186]) - Websocket server implementation `start_websocket` for LSP ([#129]) ### Changed ### Fixed [#186]: https://github.com/openlawlibrary/pygls/pull/186 [#129]: https://github.com/openlawlibrary/pygls/pull/129 ## [0.10.3] - 05/05/2021 ### Added ### Changed - Move from Azure Pipelines to Github Actions ([#182] & [#183]) - Update json-example ([#175]) - Relax text_doc type to VersionedTextDocumentIdentifier ([#174]) ### Fixed - Handle `BrokenPipeError` on shutdown ([#181]) - Exit when no more data available ([#178]) - Adding kind field to resource file operation types ([#177]) - Don't install the tests to site-packages ([#169]) - Don't serialize unwanted `"null"` values in server capabilities ([#166]) [#183]: https://github.com/openlawlibrary/pygls/pull/183 [#182]: https://github.com/openlawlibrary/pygls/pull/182 [#181]: https://github.com/openlawlibrary/pygls/pull/181 [#178]: https://github.com/openlawlibrary/pygls/pull/178 [#177]: https://github.com/openlawlibrary/pygls/pull/177 [#175]: https://github.com/openlawlibrary/pygls/pull/175 [#174]: https://github.com/openlawlibrary/pygls/pull/174 [#169]: https://github.com/openlawlibrary/pygls/pull/169 [#166]: https://github.com/openlawlibrary/pygls/pull/166 ## [0.10.2] - 03/25/2021 ### Added ### Changed - Handle lost connection; Remove psutil ([#163]) ### Fixed - Fix `pydantic` Unions type conversion ([#160]) - Fix change_notifications type (pydantic bug) ([#158]) [#163]: https://github.com/openlawlibrary/pygls/pull/163 [#160]: https://github.com/openlawlibrary/pygls/pull/160 [#158]: https://github.com/openlawlibrary/pygls/pull/158 ## [0.10.1] - 03/17/2021 ### Fixed - Remove "query" from FoldingRangeParams ([#153]) [#153]: https://github.com/openlawlibrary/pygls/pull/153 ## [0.10.0] - 03/16/2021 ### Added - New LSP types and methods ([#139]) - `pydantic` and `typeguard` deps for type-checking ([#139]) - Runtime type matching and deserialization ([#139]) ### Changed - New LSP types and methods ([#139]) - Updated docs ([#139]) ### Fixed - Periodically check client pid and exit server ([#149]) - Fix server handling of client errors ([#141]) [#149]: https://github.com/openlawlibrary/pygls/pull/149 [#141]: https://github.com/openlawlibrary/pygls/pull/141 [#139]: https://github.com/openlawlibrary/pygls/pull/139 ## [0.9.1] - 09/29/2020 ### Added - Functions to convert positions from and to utf-16 code units ([#117]) - Type definitions for `ClientInfo` and `HoverParams` ([#125]) ### Changed - Exit server normally when `ctrl+c` is pressed in command shell. - Mark deprecated `rangeLength` optional in `TextDocumentContentChangeEvent` ([#123]) - Optimize json-rpc message serialization ([#120]) - Fix `__init__()` constructors in several interface types ([#125]) - Fix valueSet type in `SymbolKindAbstract` ([#125]) ### Fixed - `coroutine` deprecation warning - use async def instead ([#136]) [#125]: https://github.com/openlawlibrary/pygls/pull/125 [#123]: https://github.com/openlawlibrary/pygls/pull/123 [#120]: https://github.com/openlawlibrary/pygls/pull/120 [#117]: https://github.com/openlawlibrary/pygls/pull/117 [#136]: https://github.com/openlawlibrary/pygls/pull/136 ## [0.9.0] - 04/20/2020 ### Changed - Fixed missing `Undo` member from `FailureHandlingKind` in types ([#98]) - Fixed `@command`, `@feature` and `@thread` decorators to retain type of wrapped functions ([#89]) ### Added - _Azure Pipelines_ build script ([#100] and [#103]) - Run tests and linters on multiple python versions with _tox_ ([#100]) - Use python enums in types module ([#92]) - Add comparisons and repr support to Range and Location types ([#90]) ### Removed - _appveyor_ build script ([#103]) [#103]: https://github.com/openlawlibrary/pygls/pull/103 [#100]: https://github.com/openlawlibrary/pygls/pull/100 [#98]: https://github.com/openlawlibrary/pygls/pull/98 [#92]: https://github.com/openlawlibrary/pygls/pull/92 [#90]: https://github.com/openlawlibrary/pygls/pull/90 [#89]: https://github.com/openlawlibrary/pygls/pull/89 ## [0.8.1] - 09/05/2019 ### Changed - Fix parsing of partial messages and those with Content-Length keyword ([#80]) - Fix Full SyncKind for servers accepting Incremental SyncKind ([#78]) [#80]: https://github.com/openlawlibrary/pygls/pull/80 [#78]: https://github.com/openlawlibrary/pygls/pull/78 ## [0.8.0] - 05/13/2019 ### Added - Add new types and features from LSP v3.14.0 ([#67]) - Add API to dynamically register/unregister client capability ([#67]) - Full text document synchronization support ([#65]) - Add more tests for `deserialize_message` function ([#61]) ### Changed - Response object should contain result OR error field ([#64]) - Fix handling parameters whose names are reserved by Python ([#56]) [#67]: https://github.com/openlawlibrary/pygls/pull/67 [#65]: https://github.com/openlawlibrary/pygls/pull/65 [#64]: https://github.com/openlawlibrary/pygls/pull/64 [#61]: https://github.com/openlawlibrary/pygls/pull/61 [#56]: https://github.com/openlawlibrary/pygls/pull/56 ## [0.7.4] - 03/21/2019 ### Added - Add Pull Request template ([#54]) ### Changed - Update dependencies ([#53]) - Fix initialization failure when no workspace is open ([#51]) [#54]: https://github.com/openlawlibrary/pygls/pull/54 [#53]: https://github.com/openlawlibrary/pygls/pull/53 [#51]: https://github.com/openlawlibrary/pygls/pull/51 ## [0.7.3] - 01/30/2019 ### Added - Add _flake8_ and _bandit_ checks to _appveyor_ script ### Changed - Start using [Keep a Changelog][keepachangelog] format. - Fix and refactor _initialize_ LSP method and add more tests - Fix _python 3.5_ compatibility - Use _python 3.5_ in _appveyor_ script ## 0.7.2 - 12/28/2018 - Fix README to use absolute paths for GitHub urls (needed for PyPi) ## 0.7.1 - 12/28/2018 - Add `publish_diagnostics` to LanguageServer - Fix validation function in json example - Correct advanced usage doc page - "pygls" -> _pygls_ everywhere in the docs ## 0.7.0 - 12/21/2018 - Open source _pygls_ ## 0.6.0 - Modules/functions/methods reorganization - Add more features/commands to json-extension example - Add unit tests to json-extension example - Update `appveyor.yml` - Small bug fixes ## 0.5.0 - Return awaitable Future object from get_configuration - Add / Remove Workspace folders bugfix - Attach loop to child watcher for UNIX systems ## 0.4.0 - Gracefully shutdown and exit server process - Disallow requests after shutdown request is received - Added more types for type hints - Improved example ## 0.3.0 - Async functions (coroutines) support - Mark function to execute it in a thread pool - Added _lsp_ types - New example - Fixed `appveyor.yml` ## 0.2.0 - Added classes for `textDocument/completion` method response ## 0.1.0 - Initial Version [keepachangelog]: https://keepachangelog.com/en/1.0.0/ [semver]: https://semver.org/spec/v2.0.0.html [Unreleased]: https://github.com/openlawlibrary/pygls/compare/v1.0.0...HEAD [1.0.0]: https://github.com/openlawlibrary/pygls/compare/v0.13.1...v1.0.0 [0.13.1]: https://github.com/openlawlibrary/pygls/compare/v0.13.0...v0.13.1 [0.13.0]: https://github.com/openlawlibrary/pygls/compare/v0.12.3...v0.13.0 [0.12.4]: https://github.com/openlawlibrary/pygls/compare/v0.12.3...v0.12.4 [0.12.3]: https://github.com/openlawlibrary/pygls/compare/v0.12.2...v0.12.3 [0.12.2]: https://github.com/openlawlibrary/pygls/compare/v0.12.1...v0.12.2 [0.12.1]: https://github.com/openlawlibrary/pygls/compare/v0.12...v0.12.1 [0.12]: https://github.com/openlawlibrary/pygls/compare/v0.11.3...v0.12 [0.11.3]: https://github.com/openlawlibrary/pygls/compare/v0.11.2...v0.11.3 [0.11.2]: https://github.com/openlawlibrary/pygls/compare/v0.11.1...v0.11.2 [0.11.1]: https://github.com/openlawlibrary/pygls/compare/v0.11.0...v0.11.1 [0.11.0]: https://github.com/openlawlibrary/pygls/compare/v0.10.3...v0.11.0 [0.10.3]: https://github.com/openlawlibrary/pygls/compare/v0.10.2...v0.10.3 [0.10.2]: https://github.com/openlawlibrary/pygls/compare/v0.10.1...v0.10.2 [0.10.1]: https://github.com/openlawlibrary/pygls/compare/v0.10.0...v0.10.1 [0.10.0]: https://github.com/openlawlibrary/pygls/compare/v0.9.1...v0.10.0 [0.9.1]: https://github.com/openlawlibrary/pygls/compare/v0.9.0...v0.9.1 [0.9.0]: https://github.com/openlawlibrary/pygls/compare/v0.8.1...v0.9.0 [0.8.1]: https://github.com/openlawlibrary/pygls/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/openlawlibrary/pygls/compare/v0.7.4...v0.8.0 [0.7.4]: https://github.com/openlawlibrary/pygls/compare/v0.7.3...v0.7.4 [0.7.3]: https://github.com/openlawlibrary/pygls/compare/v0.7.2...v0.7.3 pygls-1.3.0/CODE_OF_CONDUCT.md000066400000000000000000000063351455570166200154720ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others’ private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/4/ pygls-1.3.0/CONTRIBUTING.md000066400000000000000000000065261455570166200151260ustar00rootroot00000000000000# Contributing to _pygls_ Welcome, and thank you for your interest in contributing to _pygls_! There are many ways in which you can contribute, beyond writing code. The goal of this document is to provide a high-level overview of how you can get involved. ## Reporting Issues Have you identified a reproducible problem in _pygls_? Have a feature request? We want to hear about it! Here's how you can make reporting your issue as effective as possible. ### Look For an Existing Issue Before you create a new issue, please do a search in [open issues](https://github.com/openlawlibrary/pygls/issues) to see if the issue or feature request has already been filed. Be sure to scan through the [most popular](https://github.com/openlawlibrary/pygls/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) feature requests. If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment: * 👍 - upvote * 👎 - downvote If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below. ### Writing Good Bug Reports and Feature Requests File a single issue per problem and feature request. Do not enumerate multiple bugs or feature requests in the same issue. Do not add your issue as a comment to an existing issue unless it's for the identical input. Many issues look similar, but have different causes. The more information you can provide, the more likely someone will be successful at reproducing the issue and finding a fix. Please include the following with each issue: * Reproducible steps (1... 2... 3...) that cause the issue * What you expected to see, versus what you actually saw * Images, animations, or a link to a video showing the issue occurring, if appropriate * A code snippet that demonstrates the issue or a link to a code repository the developers can easily pull down to recreate the issue locally * **Note:** Because the developers need to copy and paste the code snippet, including a code snippet as a media file (i.e. .gif) is not sufficient. * If using VS Code, errors from the Dev Tools Console (open from the menu: Help > Toggle Developer Tools) ### Final Checklist Please remember to do the following: * [ ] Search the issue repository to ensure your report is a new issue * [ ] Simplify your code around the issue to better isolate the problem * [ ] If you are committing a PR, please update the [changelog] and [contributors] documents, as appropriate. For the [changelog], only _notable_ changes should be added following the _[Keep a Changelog][keepachangelog]_ format. Don't feel bad if the developers can't reproduce the issue right away. They will simply ask for more information! ## Thank You Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute. ## Attribution This _Contributing to pygls_ document is adapted from VS Code's _[Contributing to VS Code](https://github.com/Microsoft/vscode/blob/master/CONTRIBUTING.md)_. [changelog]: https://github.com/openlawlibrary/pygls/blob/master/CHANGELOG.md [contributors]: https://github.com/openlawlibrary/pygls/blob/master/CONTRIBUTORS.md [keepachangelog]: https://keepachangelog.com/en/1.0.0/ pygls-1.3.0/CONTRIBUTORS.md000066400000000000000000000027001455570166200151420ustar00rootroot00000000000000# Contributors (contributions) * [alcarney](https://github.com/alcarney) (99) * [anu-ka](https://github.com/anu-ka) (1) * [augb](https://github.com/augb) (35) * [berquist](https://github.com/berquist) (1) * [brettcannon](https://github.com/brettcannon) (2) * [D4N](https://github.com/D4N) (1) * [danixeee](https://github.com/danixeee) (321) * [deathaxe](https://github.com/deathaxe) (24) * [dependabot[bot]](https://github.com/apps/dependabot) (8) * [dgreisen](https://github.com/dgreisen) (4) * [DillanCMills](https://github.com/DillanCMills) (1) * [dimbleby](https://github.com/dimbleby) (11) * [dinvlad](https://github.com/dinvlad) (9) * [eirikpre](https://github.com/eirikpre) (1) * [karthiknadig](https://github.com/karthiknadig) (10) * [KOLANICH](https://github.com/KOLANICH) (3) * [LaurenceWarne](https://github.com/LaurenceWarne) (2) * [MatejKastak](https://github.com/MatejKastak) (3) * [muffinmad](https://github.com/muffinmad) (2) * [nemethf](https://github.com/nemethf) (1) * [oliversen](https://github.com/oliversen) (2) * [otreblan](https://github.com/otreblan) (1) * [pappasam](https://github.com/pappasam) (7) * [perimosocordiae](https://github.com/perimosocordiae) (1) * [perrinjerome](https://github.com/perrinjerome) (25) * [renatav](https://github.com/renatav) (2) * [RossBencina](https://github.com/RossBencina) (5) * [tombh](https://github.com/tombh) (60) * [tsugumi-sys](https://github.com/tsugumi-sys) (1) * [zanieb](https://github.com/zanieb) (5) pygls-1.3.0/HISTORY.md000066400000000000000000000027551455570166200143600ustar00rootroot00000000000000# History of [_pygls_][pygls] This is the story of [_pygls_][pygls]' inception as recounted by its original project creator, [@augb][augb]. While working at [Open Law Library][openlaw] as a programmer, we created a VS Code extension originally written in TypeScript called _Codify_. _Codify_ processes legal XML into legal code. Since our codification process was written in Python we were faced with the choice of slower performance to roundtrip from TypeScript to Python and back, or duplicating the logic in TypeScript. Neither option was really good. I had the idea of using the [Language Server Protocol (LSP)][lsp] to communicate with a Python LSP server. Existing Python language servers were focused on Python the language. We needed a generic language server since we were dealing with XML. [David Greisen][dgreisen], agreed with this approach. Thus, [_pygls_][pygls] was born. I, [@augb][augb], was the project manager for the project. Daniel Elero ([@danixeee][danixeee]) did the coding. When I left Open Law Library, Daniel took over the project for a time. It was open sourced on December 21, 2018. The announcement on Hacker News is [here][announcement]. [augb]: https://github.com/augb [announcement]: https://news.ycombinator.com/item?id=18735413 [danixeee]: https://github.com/danixeee [dgreisen]: https://github.com/dgreisen [lsp]: https://microsoft.github.io/language-server-protocol/specification [openlaw]: https://openlawlib.org/ [pygls]: https://github.com/openlawlibrary/pygls pygls-1.3.0/Implementations.md000066400000000000000000000136441455570166200163660ustar00rootroot00000000000000# Implementations Based on _pygls_ | Name | Maintainer | Repository (optional) | Notes | |--------------------------|-----------------------------------------------------------------------|----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| | _Anakin Language Server_ | [Andrii Kolomoiets](https://github.com/muffinmad) | [anakin-language-server](https://github.com/muffinmad/anakin-language-server) | Yet another Jedi Python Language Server | | _Cmake Language Server_ | [Regen100](https://github.com/regen100) | [cmake-language-server](https://github.com/regen100/cmake-language-server) | CMake LSP Implementation | _Codify Language Server_ | [Open Law Library](http://www.openlawlib.org/) | (closed source) | Used in our VS Code extension, _[Codify](https://marketplace.visualstudio.com/items?itemName=openlawlibrary.open-law-codify)_ | | _CrossHair Language Server_ | [Phillip Schanely](http://github.com/pschanely) | [CrossHair](https://github.com/pschanely/CrossHair) | Supports [this VS Code extension](https://marketplace.visualstudio.com/items?itemName=CrossHair.crosshair) for Python contract testing. | | _Esbonio Language Server_| [Alex Carney](https://github.com/alcarney) | [esbonio](https://github.com/swyddfa/esbonio) | Language server for [Sphinx](https://www.sphinx-doc.org/en/master/) documentation projects | | _Helios Language Server_ | [et9797](https://github.com/et9797) | [helios-language-server](https://github.com/et9797/helios-language-server) | Language Server for [Helios](https://github.com/Hyperion-BT/Helios) smart contract language | | _Hy Language Server_ | [Rintaro Okamura](https://github.com/rinx) | [hy-language-server](https://github.com/rinx/hy-language-server) | Hy Language Server wrapping [Jedhy](https://github.com/ekaschalk/jedhy) | | _Jedi Language Server_ | [Samuel Roeca](https://softwarejourneyman.com/pages/about.html#about) | [jedi-language-server](https://github.com/pappasam/jedi-language-server) | Python Language Server wrapping [Jedi](https://github.com/davidhalter/jedi) | | _Ruff Language Server_ | [Charlie Marsh](https://github.com/charliermarsh) | [ruff-lsp](https://github.com/charliermarsh/ruff-lsp) | Language Server for Python's [ruff](https://github.com/charliermarsh/ruff) | | _Stata Language Server_ | [Hai Bo](https://github.com/HankBO) | [stata-language-server](https://github.com/HankBO/stata-language-server) | Language Server for [Stata](https://www.stata.com/) | | _VSCode `autopep8` extension_ | [Microsoft](https://github.com/microsoft) | [vscode-autopep8](https://github.com/microsoft/vscode-autopep8) | VSCode extension for Python's [autopep8](https://github.com/hhatto/autopep8) | | _VSCode `black` extension_ | [Microsoft](https://github.com/microsoft) | [vscode-black-formatter](https://github.com/microsoft/vscode-black-formatter) | VSCode extension for Python's [black](https://github.com/psf/black) | | _VSCode `isort` extension_ | [Microsoft](https://github.com/microsoft) | [vscode-isort](https://github.com/microsoft/vscode-isort) | VSCode extension for Python's [isort](https://pycqa.github.io/isort) | | _VSCode `flake8` extension_ | [Microsoft](https://github.com/microsoft) | [vscode-flake8](https://github.com/microsoft/vscode-flake8) | VSCode extension for Python's [flake8](https://github.com/PyCQA/flake8) | | _VSCode `mypy` extension_ | [Microsoft](https://github.com/microsoft) | [vscode-mypy](https://github.com/microsoft/vscode-mypy) | VSCode extension for Python's [mypy](https://github.com/python/mypy) | | _VSCode `pylint` extension_ | [Microsoft](https://github.com/microsoft) | [vscode-pylint](https://github.com/microsoft/vscode-pylint) | VSCode extension for Python's [pylint](https://github.com/PyCQA/pylint) | | _VSCode `ufmt` extension_ | [Omnilib](https://github.com/omnilib) | [vscode-ufmt](https://github.com/omnilib/vscode-ufmt) | VSCode extension for Python's [ufmt](https://ufmt.omnilib.dev/en/stable/) | | _YARA Language Server_ | [Avast](https://github.com/avast) | [yls](https://github.com/avast/yls) | Language Server for [YARA](https://github.com/VirusTotal/yara) | | _zc.buildout Language Server_ | [JĂ©rome Perrin](https://github.com/perrinjerome) | [zc.buildout.languageserver](https://github.com/perrinjerome/vscode-zc-buildout) | Language Server for [zc.buildout](http://docs.buildout.org/en/latest/) profiles. | ## _(alphabetic by name, maintainer)_ # Tools Based on _pygls_ | Name | Maintainer | Repository (optional) | Notes | |--------------------------|-----------------------------------------------------------------------|----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| | _lsp-devtools_ | [Alex Carney](https://github.com/alcarney) | [lsp-devtools](https://github.com/swyddfa/lsp-devtools) | An experiment in building web browser inspired developer tooling for language servers | | _pytest-lsp_ | [Alex Carney](https://github.com/alcarney) | [lsp-devtools](https://github.com/swyddfa/lsp-devtools) | pytest plugin for testing language servers | pygls-1.3.0/LICENSE.txt000066400000000000000000000261471455570166200145210ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright (c) Open Law Library. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. pygls-1.3.0/PULL_REQUEST_TEMPLATE.md000066400000000000000000000012071455570166200164650ustar00rootroot00000000000000## Description (e.g. "Related to ...", etc.) _Please replace this description with a concise description of this Pull Request._ ## Code review checklist (for code reviewer to complete) - [ ] Pull request represents a single change (i.e. not fixing disparate/unrelated things in a single PR) - [ ] Title summarizes what is changing - [ ] Commit messages are meaningful (see [this][commit messages] for details) - [ ] Tests have been included and/or updated, as appropriate - [ ] Docstrings have been included and/or updated, as appropriate - [ ] Standalone docs have been updated accordingly [commit messages]: https://conventionalcommits.org/ pygls-1.3.0/README.md000066400000000000000000000072031455570166200141450ustar00rootroot00000000000000[![PyPI Version](https://img.shields.io/pypi/v/pygls.svg)](https://pypi.org/project/pygls/) ![!pyversions](https://img.shields.io/pypi/pyversions/pygls.svg) ![license](https://img.shields.io/pypi/l/pygls.svg) [![Documentation Status](https://img.shields.io/badge/docs-latest-green.svg)](https://pygls.readthedocs.io/en/latest/) # pygls: The Generic Language Server Framework _pygls_ (pronounced like "pie glass") is a pythonic generic implementation of the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/specification) for use as a foundation for writing your own [Language Servers](https://langserver.org/) in just a few lines of code. ## Quickstart ```python from pygls.server import LanguageServer from lsprotocol.types import ( TEXT_DOCUMENT_COMPLETION, CompletionItem, CompletionList, CompletionParams, ) server = LanguageServer("example-server", "v0.1") @server.feature(TEXT_DOCUMENT_COMPLETION) def completions(params: CompletionParams): items = [] document = server.workspace.get_document(params.text_document.uri) current_line = document.lines[params.position.line].strip() if current_line.endswith("hello."): items = [ CompletionItem(label="world"), CompletionItem(label="friend"), ] return CompletionList(is_incomplete=False, items=items) server.start_io() ``` Which might look something like this when you trigger autocompletion in your editor: ![completions](https://raw.githubusercontent.com/openlawlibrary/pygls/master/docs/assets/hello-world-completion.png) ## Docs and Tutorial The full documentation and a tutorial are available at . ## Projects based on _pygls_ We keep a table of all known _pygls_ [implementations](https://github.com/openlawlibrary/pygls/blob/master/Implementations.md). Please submit a Pull Request with your own or any that you find are missing. ## Alternatives The main alternative to _pygls_ is Microsoft's [NodeJS-based Generic Language Server Framework](https://github.com/microsoft/vscode-languageserver-node). Being from Microsoft it is focussed on extending VSCode, although in theory it could be used to support any editor. So this is where pygls might be a better choice if you want to support more editors, as pygls is not focussed around VSCode. There are also other Language Servers with "general" in their descriptons, or at least intentions. They are however only general in the sense of having powerful _configuration_. They achieve generality in so much as configuration is able to, as opposed to what programming (in _pygls'_ case) can achieve. * https://github.com/iamcco/diagnostic-languageserver * https://github.com/mattn/efm-langserver * https://github.com/jose-elias-alvarez/null-ls.nvim (Neovim only) ## Tests All Pygls sub-tasks require the Poetry `poe` plugin: https://github.com/nat-n/poethepoet * `poetry install --all-extras` * `poetry run poe test` * `poetry run poe test-pyodide` ## Contributing Your contributions to _pygls_ are most welcome ❤️ Please review the [Contributing](https://github.com/openlawlibrary/pygls/blob/master/CONTRIBUTING.md) and [Code of Conduct](https://github.com/openlawlibrary/pygls/blob/master/CODE_OF_CONDUCT.md) documents for how to get started. ## Donating [Open Law Library](http://www.openlawlib.org/) is a 501(c)(3) tax exempt organization. Help us maintain our open source projects and open the law to all with [sponsorship](https://github.com/sponsors/openlawlibrary). ### Supporters We would like to give special thanks to the following supporters: * [mpourmpoulis](https://github.com/mpourmpoulis) ## License Apache-2.0 pygls-1.3.0/ThirdPartyNotices.txt000066400000000000000000000057321455570166200170530ustar00rootroot00000000000000THIRD-PARTY SOFTWARE NOTICES AND INFORMATION For Open Law Library pygls This project incorporates material from the project(s) listed below (collectively, “Third Party Code”). Open Law Library is not the original author of the Third Party Code. The original copyright notice and license under which Open Law Library received such Third Party Code are set out below. This Third Party Code is licensed to you under their original license terms set forth below. Open Law Library reserves all other rights not expressly granted, whether by implication, estoppel or otherwise. 1) python-language-server (https://github.com/palantir/python-language-server) Copyright 2017 Palantir Technologies, Inc. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2) vscode-extension-samples (https://github.com/Microsoft/vscode-extension-samples) Copyright (c) Microsoft Corporation All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 3) All third party packages in node_modules folders are licensed under the licenses specified in those packages. pygls-1.3.0/cliff.toml000066400000000000000000000045511455570166200146510ustar00rootroot00000000000000[changelog] header = "" # template for the changelog body # https://tera.netlify.app/docs body = """ {% if version %}\ ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} {% else %}\ ## [unreleased] {% endif %}\ More details: https://github.com/openlawlibrary/pygls/releases/tag/{{version}} {% for group, commits in commits | group_by(attribute="group") %} ### {{ group | upper_first }} {% for commit in commits %} - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\ {% endfor %} {% endfor %}\n """ # remove the leading and trailing whitespace from the template trim = true # changelog footer footer = "" [git] # parse the commits based on https://www.conventionalcommits.org conventional_commits = true # filter out the commits that are not conventional filter_unconventional = false # TODO: Toggle after v1.0.3 as it introduces commit linting # process each line of a commit as an individual commit split_commits = false # regex for preprocessing the commit messages commit_preprocessors = [ { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/openlawlibrary/pygls/issues/${2}))"}, # replace issue numbers ] # regex for parsing and grouping commits commit_parsers = [ { message = "^feat", group = "Features" }, { message = "^fix", group = "Bug Fixes" }, { message = "^doc", group = "Documentation" }, { message = "^perf", group = "Performance" }, { message = "^refactor", group = "Refactor" }, { message = "^style", group = "Styling" }, { message = "^test", group = "Testing" }, { message = "^ci", group = "CI" }, { message = "^chore\\(release\\): prepare for", skip = true }, { message = "^chore", group = "Miscellaneous Tasks" }, { body = ".*security", group = "Security" }, ] # protect breaking changes from being skipped due to matching a skipping commit_parser protect_breaking_commits = false # filter out the commits that are not matched by commit parsers filter_commits = false # glob pattern for matching git tags tag_pattern = "v[0-9]*" # regex for skipping tags skip_tags = "v0.1.0-beta.1" # regex for ignoring tags ignore_tags = "" # sort the tags topologically topo_order = false # sort the commits inside sections by oldest/newest order sort_commits = "oldest" # limit the number of commits included in the changelog. # limit_commits = 42 pygls-1.3.0/commitlintrc.yaml000066400000000000000000000016461455570166200162630ustar00rootroot00000000000000--- # The rules below have been manually copied from @commitlint/config-conventional # and match the v1.0.0 specification: # https://www.conventionalcommits.org/en/v1.0.0/#specification # # You can remove them and uncomment the config below when the following issue is # fixed: https://github.com/conventional-changelog/commitlint/issues/613 # # extends: # - '@commitlint/config-conventional' rules: body-leading-blank: [1, always] body-max-line-length: [2, always, Infinity] footer-leading-blank: [1, always] footer-max-line-length: [2, always, 100] header-max-length: [2, always, 100] subject-case: - 2 - never - [sentence-case, start-case, pascal-case, upper-case] subject-empty: [2, never] subject-full-stop: [2, never, "."] type-case: [2, always, lower-case] type-empty: [2, never] type-enum: - 2 - always - [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] pygls-1.3.0/docs/000077500000000000000000000000001455570166200136145ustar00rootroot00000000000000pygls-1.3.0/docs/Makefile000066400000000000000000000011101455570166200152450ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = source BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)pygls-1.3.0/docs/assets/000077500000000000000000000000001455570166200151165ustar00rootroot00000000000000pygls-1.3.0/docs/assets/hello-world-completion.png000066400000000000000000000060041455570166200222230ustar00rootroot00000000000000‰PNG  IHDR˘UE¨í…iCCPICC profile(‘}‘=HĂP…O[µ";tČPťÚEEkŠP!Ô ­:Ľôš4$).Ž‚kÁÁźĹŞ‹ł®®‚ řâęâ¤č"%Ţ—ZÄxáń>λçđŢ}€żYeŞŮ“TÍ22©¤ËŻ ÁWřÁ0b蓩ωbžőuOÝTwqžĺÝ÷g *“>8ÁtĂ"Ţ žŮ´tÎűÄaV–âsâA$~äşěňç’Ă~ž6˛™yâ0±Pębą‹YŮP‰§‰ŁŠŞQľ?ç˛Ây‹łZ­łö=ů Cme™ë´"HaK!@FTa!N»FЉ ť'=ücŽ_$—L® 9P Éń˙ÁďٚũI7)”z_lűcî­†mŰvë<WZÇ_kłź¤7:ZôÚ.®;šĽ\îŁOşdHŽ ĺ/÷3ú¦<0r ¬ąskźăôČҬŇ7ŔÁ!0Q˘ěuŹw÷wĎíßžöü~‹Pr°»Â~ pHYs.#.#xĄ?vtIMEç,%Źë6ŁtEXtCommentCreated with GIMPW íIDATxÚíť{T”eŔź÷űć sa¸ČMQđ†if‚B↩ąvÝÓ¶ÖŞŰžZmŰ:íŮě´ZŰžmk­Ói+ŰŇ­<®¦eŠfyżŕ S…Éą:0fŰ7ßűî&hˇÎĐóűs>>řćýÍó<ď÷ľĎ|‰D}‚Ś0@†PjĂ?‘qD EĎŹ×Ĺ…”ăPěĐ Ď1O ÁAšI; ÇŚP „0Śéá©™ ŚăĹž—pü†›fĘS9¨Č3Ô;Śk3Ł<ĄŔq«óđŤćnÓËňđži_0Ť’šÔŚ ŮÚ¬3ů{xwĂÇÝ7şĽ‚ŇьIAÍj ľ® EšMÁ$™‹˙µ óµËŚbčżĎżň±\-^&mýěĺ·ž§!hÍD)Á„î*Xu[ž{l ĽÇžťň!“6ňŁGł”&ÍXĽ`ÎÔ$µh«ŮńńúfĎ…eOYěÔ;n/ĚiPń«q˙˙>ŮyşŁ?IÓŽ)şkNŢŘx ﵚNěÚ¸ókKE]—h&ÚÜEÇ>\˝â™U4Śg~<@t9K~wJÓ–—źjůs˙ŘdżaÉŻgĆ÷#EČRJV<4M<ĽöŮg˙ôô[›GŢ÷Ģ)ę0öÄDŻßëÉWß43î8eóýÖĘŁU.øäÜŮ[?_·»Öd şjví¨‰ÍΊéłgůÄ[óuŐ›?úşĹKY ˝f˦=žÉE9Q!z=ŢNWŰűž´áâŕ±` Hxž§ŹŃKSKţĽ˛¸;ěĄÄm•÷őjH¤>Ję8ÓężřµYÚ Ď ăŔ!†ć”r)ˇ‚Ź2đhxfÚÔnwřŚĺ«Ţ=Úy…bŚ ˇb]v§+‹ďB˛ŃÇĹ@{­ó’,Ě©SƦ)ÚjęěÂĺ×ôŠ€řŢ‹Ąśp¨y fÚ´ńČ®ÚäůfĆ)8"ŹNĎŮG'ĐnóD§gÄË ‘k4JrőCţęýű;2Ü“• äT7ţŽ…ŞŞÝíÝ"˘žúŔÓKyň··§~ďŇ9ąN!—"•© *m¬J«ŇĆČ%¨xî›Yűáµoć–Ě}bĺ’HđwÚÍ•>;E ŘíEüvű¦ĂK>őęí¬«­â7?:Ţř˝ ÔşfťoŃěe«ŞyŻÍt˛ô•ňă=©‚ůŰš»Ň#›\?ČĽ$"Záqř:ÝĽFĂc+ÓĄŐP"‘]ý[Cm#R¤"áx\čÁji~IŤÇ‚<еAÍČPŞÍ„ÔŚ ´ŐM}]]č?ţ7"LÚjFP3ň#‹˝Ţ{§/_|Łš^*±}ńöë»ZŻ´µIt9KžśPýňşŽ~î‹‘´e“gg˙0ěX[YÍćr?CÍŽh*ee9eÖâǧ_Ż{ĺőqÜÖhÓs¤żŹ @ÍZłęŔŞç ¨ćOZ±BŰđjĹ? 0&˛~·I }ÍD—łäÉń§^ZČI˘2oÍÓŐď9xÎCF˙ń.ůć×·ž9ͨé%SGTĽĎQ_łoŰŢ*ëw{\ráѲ˙śM-ž™=Z§KÎoűý ÷ŐC‡×eĚś—ź5Ć ô[N˛ôg“„ź:ę7Ku=«ł˘÷ëj낌° …ęúš™»ąÉ=#)Q.ĹŘ[roŽŃ™Žž«U$$jZ+[D&>|OfËçţyŞMTŹ)\xϲątÍ'Ő÷É6cŢ)ő{·˝ąŃćg<Ď‹×YirŃĂ‹&Yvlx©Ę"ę2ňK&ôcK4ʤ‰š "gTŕL»÷BÚVßD’ôś,)E]ot&¤Ćs|bRś­©Ů˛Śśmmů–“­>Ę—qçö#Ţ ÓoÔöD RiÝ˝qűI›ź€(^«IE–‘ť­®Ý±éx“[z¬§ź¶Š}żöŕŢ3+:đöű]čéŐ‡Vßgb¨ąW„ćsÖ¨äŐ”D‡ńÄyYrŠĆO›š”DDiĄí6[wk(µ[mL­íyßÁ–:cWČCK"ٵ˛ö¶V˙É TđQA 1ŕŁ"ĂhľRÚnlvÇ%Ž“äoh4™,1iŁSµç,Çétz˝´űýFÇčÁĺp…4©aŚ\ŢŕÄĽî.ŞŃę†Ř¸ ‹ĺŃŇÔ"KĚŮtÎî=WoKž2%®˝±ŃϵîqĹó&Ĺ*8"ŃŽ)úE^ę߸B‹ÁéđčFĄä„ČŐj]Ői’Y4{´VÂÉŁRsK˛‡Ŕţ6ĎqĽTŞüŞÓ믒úí„Y7%4ÜVeńPýÍS”Ć}ĺµ@;ĚŐ-˛qӊ︭(?3‘™ľ(ÝYu±M”hÓroŠ0奄Âňë8ďTM.ż 0?g´˛őt]›Ŕ»É,$M+š7fî8µíH•;Íŕ>v˘ŃŰŹ Ýîm©´«»:<Ôĺk=é¨;ŮĺĽâ”ße·‡QÔ‚~Ü’ŕF$2\k3‚šÔŚšÔŚ f5#?Ă«­€DŽĚ˙eq^z´T xlßl^»ű¬?„ł4“î]^˙Méëźš…Ľđú‡°I(T¤iĹwN—~ç…Ł‘©,Äg–ťmM-ÔîůIźeMBˇ”*>Ai©¬nő1čô„xóšwżgţ©Ż›„®=ĂĐO{čü‰L%•Ä/}şćmkK+=ě*ť@’qwýeÁ)/•:ľ\űZąĄg@Ą17Ü6ë–IIú>ŕ0W|^ľĎč¦ {÷Ę››ß;¦Ľ%'=NŁSѦýź–íiđ1€đű‡°I(¬H°zgÍ!}룿K?öÖ{Gľ˙ŚŢ;‚gĘţúwOYüDÁĺ'Üx˙s¤Ąom2;™:˝ŕW÷- ţ{ý+ŕS ćZ·|´á › Kžąě‘9y5ď|ŐJűĐ?„MBJť@\BÖŚ ëWeLÎ ±Ă¸ŹQäčďF‹uťŘ¶ë”M€@Ë·&·>ÎŔAźú‡®W“ĐϤ7ĽN ࢢ˘¤‰ł]žßvŇéčţ§©—ü&FôÂs•.ô}^˙Ą‚Ź\Ú$„7T×±8ťNżyďšŇăa|6zú‡¬îńs¸<Ň«ć–Ę}¦ÄŮ÷NkPpD¦59=öZ!1ű‡0š{ŹLgĺ‡ëE…EK—ß®ö†ŞŠÝµ—>!©·“¶ëhuÚÄţŘ—gKM|GőwIŔ{´ľě™–®şŕŔUil’`“‚µ5#Ă!S›‘>µ“6‚šÔŚ f5#¨AÍjFP3jFP3‚šÔŚ\wD"ĂQöüT+ş<(Ń—IEND®B`‚pygls-1.3.0/docs/make.bat000066400000000000000000000013641455570166200152250ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=source set BUILDDIR=build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd pygls-1.3.0/docs/requirements.txt000066400000000000000000000441331455570166200171050ustar00rootroot00000000000000alabaster==0.7.13 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3 \ --hash=sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2 attrs==23.1.0 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \ --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015 babel==2.12.1 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610 \ --hash=sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455 cattrs==23.1.2 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:b2bb14311ac17bed0d58785e5a60f022e5431aca3932e3fc5cc8ed8639de50a4 \ --hash=sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657 certifi==2023.7.22 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 charset-normalizer==3.2.0 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96 \ --hash=sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c \ --hash=sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710 \ --hash=sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706 \ --hash=sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020 \ --hash=sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252 \ --hash=sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad \ --hash=sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329 \ --hash=sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a \ --hash=sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f \ --hash=sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6 \ --hash=sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4 \ --hash=sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a \ --hash=sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46 \ --hash=sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2 \ --hash=sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23 \ --hash=sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace \ --hash=sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd \ --hash=sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982 \ --hash=sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10 \ --hash=sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2 \ --hash=sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea \ --hash=sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09 \ --hash=sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5 \ --hash=sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149 \ --hash=sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489 \ --hash=sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9 \ --hash=sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80 \ --hash=sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592 \ --hash=sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3 \ --hash=sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6 \ --hash=sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed \ --hash=sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c \ --hash=sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200 \ --hash=sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a \ --hash=sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e \ --hash=sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d \ --hash=sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6 \ --hash=sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623 \ --hash=sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669 \ --hash=sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3 \ --hash=sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa \ --hash=sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9 \ --hash=sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2 \ --hash=sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f \ --hash=sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1 \ --hash=sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4 \ --hash=sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a \ --hash=sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8 \ --hash=sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3 \ --hash=sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029 \ --hash=sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f \ --hash=sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959 \ --hash=sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22 \ --hash=sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7 \ --hash=sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952 \ --hash=sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346 \ --hash=sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e \ --hash=sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d \ --hash=sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299 \ --hash=sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd \ --hash=sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a \ --hash=sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3 \ --hash=sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037 \ --hash=sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94 \ --hash=sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c \ --hash=sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858 \ --hash=sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a \ --hash=sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449 \ --hash=sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c \ --hash=sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918 \ --hash=sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1 \ --hash=sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c \ --hash=sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac \ --hash=sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa colorama==0.4.6 ; python_full_version >= "3.7.9" and python_version < "4" and sys_platform == "win32" \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 docutils==0.18.1 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c \ --hash=sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06 exceptiongroup==1.1.3 ; python_full_version >= "3.7.9" and python_version < "3.11" \ --hash=sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9 \ --hash=sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3 idna==3.4 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 imagesize==1.4.1 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \ --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a importlib-metadata==6.7.0 ; python_full_version >= "3.7.9" and python_version < "3.10" \ --hash=sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4 \ --hash=sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5 jinja2==3.1.2 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 lsprotocol==2023.0.0a3 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:2896c5a30c34846e3d5687e35715961f49bf7b92a36e4fb2b707ff65f19087f7 \ --hash=sha256:d704e4e00419f74bece9795de4b34d02aa555fc0131fec49f59ac9eb46816e51 markupsafe==2.1.3 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \ --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \ --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \ --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \ --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \ --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \ --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \ --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \ --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \ --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \ --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \ --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \ --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \ --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \ --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \ --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \ --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \ --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \ --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \ --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \ --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \ --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \ --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \ --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \ --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \ --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \ --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \ --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \ --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \ --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \ --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \ --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \ --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \ --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \ --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \ --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \ --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \ --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \ --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \ --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \ --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \ --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \ --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \ --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \ --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \ --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \ --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \ --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \ --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 packaging==23.1 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \ --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f pygments==2.16.1 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \ --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29 pytz==2023.3 ; python_full_version >= "3.7.9" and python_version < "3.9" \ --hash=sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588 \ --hash=sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb requests==2.31.0 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 snowballstemmer==2.2.0 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \ --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a sphinx-rtd-theme==1.3.0 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0 \ --hash=sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931 sphinx==5.3.0 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d \ --hash=sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5 sphinxcontrib-applehelp==1.0.2 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a \ --hash=sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58 sphinxcontrib-devhelp==1.0.2 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e \ --hash=sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4 sphinxcontrib-htmlhelp==2.0.0 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07 \ --hash=sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2 sphinxcontrib-jquery==4.1 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a \ --hash=sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae sphinxcontrib-jsmath==1.0.1 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \ --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8 sphinxcontrib-qthelp==1.0.3 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72 \ --hash=sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6 sphinxcontrib-serializinghtml==1.1.5 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd \ --hash=sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952 typeguard==3.0.2 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:bbe993854385284ab42fd5bd3bee6f6556577ce8b50696d6cb956d704f286c8e \ --hash=sha256:fee5297fdb28f8e9efcb8142b5ee219e02375509cd77ea9d270b5af826358d5a typing-extensions==4.7.1 ; python_full_version >= "3.7.9" and python_version < "3.11" \ --hash=sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36 \ --hash=sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2 urllib3==2.0.4 ; python_full_version >= "3.7.9" and python_version < "4" \ --hash=sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11 \ --hash=sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4 zipp==3.15.0 ; python_full_version >= "3.7.9" and python_version < "3.10" \ --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 pygls-1.3.0/docs/source/000077500000000000000000000000001455570166200151145ustar00rootroot00000000000000pygls-1.3.0/docs/source/conf.py000066400000000000000000000213741455570166200164220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) import importlib.metadata import re from docutils import nodes # -- Project information ----------------------------------------------------- project = "pygls" copyright = "Open Law Library" author = "Open Law Library" # The short X.Y version version = importlib.metadata.version("pygls") # The full version, including alpha/beta/rc tags release = version title = "pygls Documentation" description = "a pythonic generic language server" # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", ] autodoc_member_order = "groupwise" autodoc_typehints = "description" autodoc_typehints_description_target = "all" intersphinx_mapping = { "python": ("https://docs.python.org/3/", None), } # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = ".rst" # The master toctree document. master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. pygments_style = None # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = "pyglsdoc" # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, "pygls.tex", title, author, "manual"), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [(master_doc, "pygls", description, [author], 1)] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, "pygls", title, author, "pygls", description, "Miscellaneous"), ] # -- Options for Epub output ------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project # The unique identifier of the text. This can be a ISBN number # or the project homepage. # # epub_identifier = '' # A unique identification for the text. # # epub_uid = '' # A list of files that should not be packed into the epub file. epub_exclude_files = ["search.html"] def lsp_role(name, rawtext, text, lineno, inliner, options={}, content=[]): """Link to sections within the lsp specification.""" anchor = text.replace("$/", "").replace("/", "_") ref = f"https://microsoft.github.io/language-server-protocol/specification.html#{anchor}" node = nodes.reference(rawtext, text, refuri=ref, **options) return [node], [] CODE_FENCE_PATTERN = re.compile(r"```(\w+)?") LINK_PATTERN = re.compile(r"\{@link ([^}]+)\}") LITERAL_PATTERN = re.compile(r"(?`` with ``**LSP v**`` - Replaces ``{@link }`` with ``:class:`~lsprotocol.types.` `` - Replaces markdown hyperlink with reStructuredText equivalent - Replaces inline markdown code (single "`") with reStructuredText inline code (double "`") - Inserts the required newline before a bulleted list - Replaces code fences with code blocks - Fixes indentation """ line_breaks = [] code_fences = [] for i, line in enumerate(lines): if line.startswith("- "): line_breaks.append(i) # Does the line need dedenting? if line.startswith(" " * 4) and not lines[i - 1].startswith(" "): # Be sure to modify the original list *and* the line the rest of the # loop will use. line = lines[i][4:] lines[i] = line if (match := SINCE_PATTERN.search(line)) is not None: start, end = match.span() lines[i] = "".join([line[:start], f"**LSP v{match.group(1)}**", line[end:]]) if (match := LINK_PATTERN.search(line)) is not None: start, end = match.span() item = match.group(1) lines[i] = "".join( [line[:start], f":class:`~lsprotocol.types.{item}`", line[end:]] ) if (match := MD_LINK_PATTERN.search(line)) is not None: start, end = match.span() text = match.group(1) target = match.group(2) line = "".join([line[:start], f"`{text} <{target}>`__", line[end:]]) lines[i] = line if (match := LITERAL_PATTERN.search(line)) is not None: start, end = match.span() lines[i] = "".join([line[:start], f"`{match.group(0)}` ", line[end:]]) if (match := CODE_FENCE_PATTERN.match(line)) is not None: open_ = len(code_fences) % 2 == 0 lang = match.group(1) or "" if open_: code_fences.append((i, lang)) line_breaks.extend([i, i + 1]) else: code_fences.append(i) # Rewrite fenced code blocks open_ = -1 for fence in code_fences: if isinstance(fence, tuple): open_ = fence[0] + 1 lines[fence[0]] = f".. code-block:: {fence[1]}" else: # Indent content for j in range(open_, fence): lines[j] = f" {lines[j]}" lines[fence] = "" # Insert extra line breaks for offset, line in enumerate(line_breaks): lines.insert(line + offset, "") def setup(app): app.add_role("lsp", lsp_role) app.connect("autodoc-process-docstring", process_docstring) pygls-1.3.0/docs/source/index.rst000066400000000000000000000024501455570166200167560ustar00rootroot00000000000000.. pygls documentation master file, created by sphinx-quickstart on Sun Nov 25 16:16:27 2018. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. *pygls* ======= `pygls`_ (pronounced like “pie glass”) is a generic implementation of the `Language Server Protocol`_ written in the Python programming language. It allows you to write your own `language server`_ in just a few lines of code. Features -------- - cross-platform support - TCP/IP and STDIO communication - runs in asyncio event loop - register LSP features and custom commands as: - asynchronous functions (coroutines) - synchronous functions - functions that will be executed in separate thread - thread management - in-memory workspace with *full* and *incremental* document updates - type-checking - good test coverage Python Versions --------------- *pygls* works with Python 3.8+. User Guide ---------- .. toctree:: :maxdepth: 2 pages/getting_started pages/tutorial pages/user-guide pages/testing pages/migrating-to-v1 pages/reference .. _Language Server Protocol: https://microsoft.github.io/language-server-protocol/specification .. _Language server: https://langserver.org/ .. _pygls: https://github.com/openlawlibrary/pygls pygls-1.3.0/docs/source/pages/000077500000000000000000000000001455570166200162135ustar00rootroot00000000000000pygls-1.3.0/docs/source/pages/getting_started.rst000066400000000000000000000051671455570166200221450ustar00rootroot00000000000000Getting Started =============== This document explains how to install *pygls* and get started writing language servers that are based on it. .. note:: Before going any further, if you are not familiar with *language servers* and *Language Server Protocol*, we recommend reading following articles: - `Language Server Protocol Overview `_ - `Language Server Protocol Specification `_ - `Language Server Protocol SDKs `_ Installation ------------ To get the latest release from *PyPI*, simply run: .. code:: console pip install pygls Alternatively, *pygls* source code can be downloaded from our `GitHub`_ page and installed with following command: .. code:: console pip install git+https://github.com/openlawlibrary/pygls Quick Start ----------- Spin the Server Up ~~~~~~~~~~~~~~~~~~ *pygls* is a language server that can be started without writing any additional code: .. code:: python from pygls.server import LanguageServer server = LanguageServer('example-server', 'v0.1') server.start_tcp('127.0.0.1', 8080) After running the code above, server will start listening for incoming ``Json RPC`` requests on ``http://127.0.0.1:8080``. Register Features and Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *pygls* comes with an API for registering additional features like ``code completion``, ``find all references``, ``go to definition``, etc. .. code:: python @server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=[','])) def completions(params: CompletionParams): """Returns completion items.""" return CompletionList( is_incomplete=False, items=[ CompletionItem(label='Item1'), CompletionItem(label='Item2'), CompletionItem(label='Item3'), ] ) … as well as custom commands: .. code:: python @server.command('myVerySpecialCommandName') def cmd_return_hello_world(ls, *args): return 'Hello World!' See the :mod:`lsprotocol.types` module for the complete and canonical list of available features. Tutorial -------- We recommend completing the :ref:`tutorial `, especially if you haven't worked with language servers before. User Guide ---------- To reveal the full potential of *pygls* (``thread management``, ``coroutines``, ``multi-root workspace``, ``TCP/STDIO communication``, etc.) keep reading. .. _GitHub: https://github.com/openlawlibrary/pygls pygls-1.3.0/docs/source/pages/migrating-to-v1.rst000066400000000000000000000320321455570166200216720ustar00rootroot00000000000000Migrating to v1.0 ================= The most notable change of the ``v1.0`` release of ``pygls`` is the removal of its hand written LSP type and method definitions in favour of relying on the types provided by the `lsprotocol`_ library which are automatically generated from the LSP specification. As as side effect this has also meant the removal of `pydantic`_ as a dependency, since ``lsprotocol`` uses `attrs`_ and `cattrs`_ for serialisation and validation. This guide outlines how to adapt an existing server to the breaking changes introduced in this release. Known Migrations ---------------- You may find insight and inspiration from these projects that have already successfully migrated to v1: * `jedi-language-server`_ * `vscode-ruff`_ * `esbonio`_ * `yara-language-server`_ Updating Imports ---------------- The ``pygls.lsp.methods`` and ``pygls.lsp.types`` modules no longer exist. Instead, all types and method names should now be imported from the ``lsprotocol.types`` module. Additionally, the following types and constants have been renamed. ================================================================== ============== pygls lsprotocol ================================================================== ============== ``CODE_ACTION`` ``TEXT_DOCUMENT_CODE_ACTION`` ``CODE_LENS`` ``TEXT_DOCUMENT_CODE_LENS`` ``COLOR_PRESENTATION`` ``TEXT_DOCUMENT_COLOR_PRESENTATION`` ``COMPLETION`` ``TEXT_DOCUMENT_COMPLETION`` ``DECLARATION`` ``TEXT_DOCUMENT_DECLARATION`` ``DEFINITION`` ``TEXT_DOCUMENT_DEFINITION`` ``DOCUMENT_COLOR`` ``TEXT_DOCUMENT_DOCUMENT_COLOR`` ``DOCUMENT_HIGHLIGHT`` ``TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT`` ``DOCUMENT_LINK`` ``TEXT_DOCUMENT_DOCUMENT_LINK`` ``DOCUMENT_SYMBOL`` ``TEXT_DOCUMENT_DOCUMENT_SYMBOL`` ``FOLDING_RANGE`` ``TEXT_DOCUMENT_FOLDING_RANGE`` ``FORMATTING`` ``TEXT_DOCUMENT_FORMATTING`` ``HOVER`` ``TEXT_DOCUMENT_HOVER`` ``IMPLEMENTATION`` ``TEXT_DOCUMENT_IMPLEMENTATION`` ``LOG_TRACE_NOTIFICATION`` ``LOG_TRACE`` ``ON_TYPE_FORMATTING`` ``TEXT_DOCUMENT_ON_TYPE_FORMATTING`` ``PREPARE_RENAME`` ``TEXT_DOCUMENT_PREPARE_RENAME`` ``PROGRESS_NOTIFICATION`` ``PROGRESS`` ``RANGE_FORMATTING`` ``TEXT_DOCUMENT_RANGE_FORMATTING`` ``REFERENCES`` ``TEXT_DOCUMENT_REFERENCES`` ``RENAME`` ``TEXT_DOCUMENT_RENAME`` ``SELECTION_RANGE`` ``TEXT_DOCUMENT_SELECTION_RANGE`` ``SET_TRACE_NOTIFICATION`` ``SET_TRACE`` ``SIGNATURE_HELP`` ``TEXT_DOCUMENT_SIGNATURE_HELP`` ``TEXT_DOCUMENT_CALL_HIERARCHY_INCOMING_CALLS`` ``CALL_HIERARCHY_INCOMING_CALLS`` ``TEXT_DOCUMENT_CALL_HIERARCHY_OUTGOING_CALLS`` ``CALL_HIERARCHY_OUTGOING_CALLS`` ``TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE`` ``TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY`` ``TYPE_DEFINITION`` ``TEXT_DOCUMENT_TYPE_DEFINITION`` ``WORKSPACE_FOLDERS`` ``WORKSPACE_WORKSPACE_FOLDERS`` ``ApplyWorkspaceEditResponse`` ``ApplyWorkspaceEditResult`` ``ClientInfo`` ``InitializeParamsClientInfoType`` ``CodeActionDisabled`` ``CodeActionDisabledType`` ``CodeActionLiteralSupportActionKindClientCapabilities`` ``CodeActionClientCapabilitiesCodeActionLiteralSupportTypeCodeActionKindType`` ``CodeActionLiteralSupportClientCapabilities`` ``CodeActionClientCapabilitiesCodeActionLiteralSupportType`` ``CompletionItemClientCapabilities`` ``CompletionClientCapabilitiesCompletionItemType`` ``CompletionItemKindClientCapabilities`` ``CompletionClientCapabilitiesCompletionItemKindType`` ``CompletionTagSupportClientCapabilities`` ``CompletionClientCapabilitiesCompletionItemTypeTagSupportType`` ``DocumentSymbolCapabilitiesTagSupport`` ``DocumentSymbolClientCapabilitiesTagSupportType`` ``InsertTextModeSupportClientCapabilities`` ``CompletionClientCapabilitiesCompletionItemTypeInsertTextModeSupportType`` ``MarkedStringType`` ``MarkedString`` ``MarkedString`` ``MarkedString_Type1`` ``PrepareRename`` ``PrepareRenameResult_Type1`` ``PublishDiagnosticsTagSupportClientCapabilities`` ``PublishDiagnosticsClientCapabilitiesTagSupportType`` ``ResolveSupportClientCapabilities`` ``CodeActionClientCapabilitiesResolveSupportType`` ``SemanticTokensRequestsFull`` ``SemanticTokensRegistrationOptionsFullType1`` ``SemanticTokensRequests`` ``SemanticTokensClientCapabilitiesRequestsType`` ``ServerInfo`` ``InitializeResultServerInfoType`` ``ShowMessageRequestActionItem`` ``ShowMessageRequestClientCapabilitiesMessageActionItemType`` ``SignatureHelpInformationClientCapabilities`` ``SignatureHelpClientCapabilitiesSignatureInformationType`` ``SignatureHelpInformationParameterInformationClientCapabilities`` ``SignatureHelpClientCapabilitiesSignatureInformationTypeParameterInformationType`` ``TextDocumentContentChangeEvent`` ``TextDocumentContentChangeEvent_Type1`` ``TextDocumentContentChangeTextEvent`` ``TextDocumentContentChangeEvent_Type2`` ``TextDocumentSyncOptionsServerCapabilities`` ``TextDocumentSyncOptions`` ``Trace`` ``TraceValues`` ``URI`` ``str`` ``WorkspaceCapabilitiesSymbolKind`` ``WorkspaceSymbolClientCapabilitiesSymbolKindType`` ``WorkspaceCapabilitiesTagSupport`` ``WorkspaceSymbolClientCapabilitiesTagSupportType`` ``WorkspaceFileOperationsServerCapabilities`` ``FileOperationOptions`` ``WorkspaceServerCapabilities`` ``ServerCapabilitiesWorkspaceType`` ================================================================== ============== Custom Models ------------- One of the most obvious changes is the switch to `attrs`_ and `cattrs`_ for serialization and deserialisation. This means that any custom models used by your language server will need to be converted to an ``attrs`` style class. .. code-block:: python # Before from pydantic import BaseModel, Field class ExampleConfig(BaseModel): build_dir: Optional[str] = Field(None, alias="buildDir") builder_name: str = Field("html", alias="builderName") conf_dir: Optional[str] = Field(None, alias="confDir") .. code-block:: python # After import attrs @attrs.define class ExampleConfig: build_dir: Optional[str] = attrs.field(default=None) builder_name: str = attrs.field(default="html") conf_dir: Optional[str] = attrs.field(default=None) Pygls provides a default `converter`_ that it will use when converting your models to/from JSON, which should be sufficient for most scenarios. .. code-block:: pycon >>> from pygls.protocol import default_converter >>> converter = default_converter() >>> config = ExampleConfig(builder_name='epub', conf_dir='/path/to/conf') >>> converter.unstructure(config) {'builderName': 'epub', 'confDir': '/path/to/conf'} # Note how snake_case is converted to camelCase >>> converter.structure({'builderName': 'epub', 'confDir': '/path/to/conf'}, ExampleConfig) ExampleConfig(build_dir=None, builder_name='epub', conf_dir='/path/to/conf') However, depending on the complexity of your type definitions you may find the default converter fail to parse some of your types. .. code-block:: pycon >>> from typing import Literal, Union >>> @attrs.define ... class ExampleConfig: ... num_jobs: Union[Literal["auto"], int] = attrs.field(default='auto') ... >>> converter.structure({'numJobs': 'auto'}, ExampleConfig) + Exception Group Traceback (most recent call last): | File "", line 1, in | File "/.../python3.10/site-packages/cattrs/converters.py", li ne 309, in structure | return self._structure_func.dispatch(cl)(obj, cl) | File "", line 10, in structure_ExampleConfig | if errors: raise __c_cve('While structuring ' + 'ExampleConfig', errors, __cl) | cattrs.errors.ClassValidationError: While structuring ExampleConfig (1 sub-exception) +-+---------------- 1 ---------------- | Traceback (most recent call last): | File "", line 6, in structure_ExampleConfig | res['num_jobs'] = __c_structure_num_jobs(o['numJobs'], __c_type_num_jobs) | File "/.../python3.10/site-packages/cattrs/converters.py", line 377, in _structure_error | raise StructureHandlerNotFoundError(msg, type_=cl) | cattrs.errors.StructureHandlerNotFoundError: Unsupported type: typing.Union[typing.Literal['auto'], int]. Register a structure hook for it. | Structuring class ExampleConfig @ attribute num_jobs +------------------------------------ In which case you can extend the converter provided by ``pygls`` with your own `structure hooks`_ .. code-block:: python from pygls.protocol import default_converter def custom_converter(): converter = default_converter() converter.register_structure_hook(Union[Literal['auto', int], lambda obj, _: obj) return converter You can then override the default converter used by ``pygls`` when constructing your language server instance .. code-block:: python server = LanguageServer( name="my-language-server", version="v1.0", converter_factory=custom_converter ) See the `hooks.py`_ module in ``lsprotocol`` for some example structure hooks Miscellaneous ------------- Mandatory ``name`` and ``version`` """""""""""""""""""""""""""""""""" It is now necessary to provide a name and version when constructing an instance of the ``LanguageServer`` class .. code-block:: python from pygls.server import LanguageServer server = LanguageServer(name="my-language-server", version="v1.0") ``ClientCapabilities.get_capability`` is now ``get_capability`` """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .. code-block:: python # Before from pygls.lsp.types import ClientCapabilities client_capabilities = ClientCapabilities() commit_character_support = client_capabilities.get_capability( "text_document.completion.completion_item.commit_characters_support", False ) .. code-block:: python # After from lsprotocol.types import ClientCapabilities from pygls.capabilities import get_capability client_capabilities = ClientCapabilities() commit_character_support = get_capability( client_capabilities, "text_document.completion.completion_item.commit_characters_support", False ) .. _attrs: https://www.attrs.org/en/stable/index.html .. _cattrs: https://cattrs.readthedocs.io/en/stable/ .. _converter: https://cattrs.readthedocs.io/en/stable/converters.html .. _hooks.py: https://github.com/microsoft/lsprotocol/blob/main/lsprotocol/_hooks.py .. _lsprotocol: https://github.com/microsoft/lsprotocol .. _pydantic: https://pydantic-docs.helpmanual.io/ .. _structure hooks: https://cattrs.readthedocs.io/en/stable/structuring.html#registering-custom-structuring-hooks .. _jedi-language-server: https://github.com/pappasam/jedi-language-server/pull/230 .. _yara-language-server: https://github.com/avast/yls/pull/34 .. _vscode-ruff: https://github.com/charliermarsh/vscode-ruff/pull/37 .. _esbonio: https://github.com/swyddfa/esbonio/pull/484 pygls-1.3.0/docs/source/pages/reference.rst000066400000000000000000000001241455570166200207000ustar00rootroot00000000000000API Reference ============= .. toctree:: :glob: :maxdepth: 2 reference/* pygls-1.3.0/docs/source/pages/reference/000077500000000000000000000000001455570166200201515ustar00rootroot00000000000000pygls-1.3.0/docs/source/pages/reference/clients.rst000066400000000000000000000002111455570166200223360ustar00rootroot00000000000000Clients ======= .. autoclass:: pygls.lsp.client.BaseLanguageClient :members: .. autoclass:: pygls.client.JsonRPCClient :members: pygls-1.3.0/docs/source/pages/reference/protocol.rst000066400000000000000000000003071455570166200225440ustar00rootroot00000000000000Protocol ======== .. autoclass:: pygls.protocol.LanguageServerProtocol :members: .. autoclass:: pygls.protocol.JsonRPCProtocol :members: .. autofunction:: pygls.protocol.default_converter pygls-1.3.0/docs/source/pages/reference/servers.rst000066400000000000000000000001751455570166200223770ustar00rootroot00000000000000Servers ======= .. autoclass:: pygls.server.LanguageServer :members: .. autoclass:: pygls.server.Server :members: pygls-1.3.0/docs/source/pages/reference/types.rst000066400000000000000000000003101455570166200220410ustar00rootroot00000000000000Types ===== LSP type definitions in ``pygls`` are provided by the `lsprotocol `__ library .. automodule:: lsprotocol.types :members: :undoc-members: pygls-1.3.0/docs/source/pages/reference/workspace.rst000066400000000000000000000002061455570166200226770ustar00rootroot00000000000000Workspace ========= .. autoclass:: pygls.workspace.TextDocument :members: .. autoclass:: pygls.workspace.Workspace :members: pygls-1.3.0/docs/source/pages/testing.rst000066400000000000000000000015061455570166200204240ustar00rootroot00000000000000.. _testing: Testing ======= Unit Tests ---------- Writing unit tests for registered features and commands are easy and you don't have to mock the whole language server. If you skipped the advanced usage page, take a look at :ref:`passing language server instance ` section for more details. Integration Tests ----------------- Integration tests coverage includes the whole workflow, from sending the client request, to getting the result from the server. Since the *Language Server Protocol* defines bidirectional communication between the client and the server, we used *pygls* to simulate the client and send desired requests to the server. To get a better understanding of how to set it up, take a look at our test `fixtures`_. .. _fixtures: https://github.com/openlawlibrary/pygls/blob/main/tests/conftest.py pygls-1.3.0/docs/source/pages/tutorial.rst000066400000000000000000000162571455570166200206230ustar00rootroot00000000000000.. _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 `__ 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 `_ editor * `Python 3.8+ `_ * `vscode-python `_ extension * A clone of the `pygls `_ repository .. note:: If you have created virtual environment, make sure that you have *pygls* installed and `selected appropriate python interpreter `_ 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 ` 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 pygls-1.3.0/docs/source/pages/user-guide.rst000066400000000000000000000445711455570166200210310ustar00rootroot00000000000000.. _user-guide: User Guide ========== Language Server --------------- The language server is responsible for managing the connection with the client as well as sending and receiving messages over the `Language Server Protocol `__ which is based on the `Json RPC protocol `__. Connections ~~~~~~~~~~~ *pygls* supports :ref:`ls-tcp`, :ref:`ls-stdio` and :ref:`ls-websocket` connections. .. _ls-tcp: TCP ^^^ TCP connections are usually used while developing the language server. This way the server can be started in *debug* mode separately and wait for the client connection. .. note:: Server should be started **before** the client. The code snippet below shows how to start the server in *TCP* mode. .. code:: python from pygls.server import LanguageServer server = LanguageServer('example-server', 'v0.1') server.start_tcp('127.0.0.1', 8080) .. _ls-stdio: STDIO ^^^^^ STDIO connections are useful when client is starting the server as a child process. This is the way to go in production. The code snippet below shows how to start the server in *STDIO* mode. .. code:: python from pygls.server import LanguageServer server = LanguageServer('example-server', 'v0.1') server.start_io() .. _ls-websocket: WEBSOCKET ^^^^^^^^^ WEBSOCKET connections are used when you want to expose language server to browser based editors. The code snippet below shows how to start the server in *WEBSOCKET* mode. .. code:: python from pygls.server import LanguageServer server = LanguageServer('example-server', 'v0.1') server.start_ws('0.0.0.0', 1234) Logging ~~~~~~~ Logs are useful for tracing client requests, finding out errors and measuring time needed to return results to the client. *pygls* uses built-in python *logging* module which has to be configured before server is started. Official documentation about logging in python can be found `here `__. Below is the minimal setup to setup logging in *pygls*: .. code:: python import logging from pygls.server import LanguageServer logging.basicConfig(filename='pygls.log', filemode='w', level=logging.DEBUG) server = LanguageServer('example-server', 'v0.1') server.start_io() Overriding ``LanguageServerProtocol`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you have a reason to override the existing ``LanguageServerProtocol`` class, you can do that by inheriting the class and passing it to the ``LanguageServer`` constructor. Custom Error Reporting ~~~~~~~~~~~~~~~~~~~~~~ The default :class:`~pygls.server.LanguageServer` will send a :lsp:`window/showMessage` notification to the client to display any uncaught exceptions in the server. To override this behaviour define your own :meth:`~pygls.server.LanguageServer.report_server_error` method like so: .. code:: python class CustomLanguageServer(LanguageServer): def report_server_error(self, error: Exception, source: Union[PyglsError, JsonRpcException]): pass Handling Client Messages ------------------------ .. admonition:: Requests vs Notifications Unlike a *request*, a *notification* message has no ``id`` field and the server *must not* reply to it. This means that, even if you return a result inside a handler function for a notification, the result won't be passed to the client. The ``Language Server Protocol``, unlike ``Json RPC``, allows bidirectional communication between the server and the client. For the majority of the time, a language server will be responding to requests and notifications sent from the client. *pygls* refers to the handlers for all of these messages as *features* with one exception. The Language Server protocol allows a server to define named methods that a client can invoke by sending a :lsp:`workspace/executeCommand` request. Unsurprisingly, *pygls* refers to these named methods a *commands*. *Built-In* Features ~~~~~~~~~~~~~~~~~~~ *pygls* comes with following predefined set of handlers for the following `Language Server Protocol `__ (LSP) features: .. note:: *Built-in* features in most cases should *not* be overridden. If you need to do some additional processing of one of the messages listed below, register a feature with the same name and your handler will be called immediately after the corresponding built-in feature. **Lifecycle Messages** - The :lsp:`initialize` request is sent as a first request from client to the server to setup their communication. *pygls* automatically computes registered LSP capabilities and sends them as part of the :class:`~lsprotocol.types.InitializeResult` response. - The :lsp:`shutdown` request is sent from the client to the server to ask the server to shutdown. - The :lsp:`exit` notification is sent from client to the server to ask the server to exit the process. *pygls* automatically releases all resources and stops the process. **Text Document Synchronization** - The :lsp:`textDocument/didOpen` notification will tell *pygls* to create a document in the in-memory workspace which will exist as long as the document is opened in editor. - The :lsp:`textDocument/didChange` notification will tell *pygls* to update the document text. *pygls* supports *full* and *incremental* document changes. - The :lsp:`textDocument/didClose` notification will tell *pygls* to remove a document from the in-memory workspace. **Notebook Document Synchronization** - The :lsp:`notebookDocument/didOpen` notification will tell *pygls* to create a notebook document in the in-memory workspace which will exist as long as the document is opened in editor. - The :lsp:`notebookDocument/didChange` notification will tell *pygls* to update the notebook document include its content, metadata, execution results and cell structure. - The :lsp:`notebookDocument/didClose` notification will tell *pygls* to remove the notebook from the in-memory workspace. **Miscellanous** - The :lsp:`workspace/didChangeWorkspaceFolders` notification will tell *pygls* to update in-memory workspace folders. - The :lsp:`workspace/executeCommand` request will tell *pygls* to execute a custom command. - The :lsp:`$/setTrace` notification tells *pygls* to update the server's :class:`TraceValue `. .. _ls-handlers: Registering Handlers ~~~~~~~~~~~~~~~~~~~~ .. seealso:: It's recommeded that you follow the :ref:`tutorial ` before reading this section. - The :func:`~pygls.server.LanguageServer.feature` decorator is used to register a handler for a given LSP message. - The :func:`~pygls.server.LanguageServer.command` decorator is used to register a named command. The following applies to both feature and command handlers. Language servers using *pygls* run in an *asyncio event loop*. They *asynchronously* listen for incoming messages and, depending on the way handler is registered, apply different execution strategies to process the message. Depending on the use case, handlers can be registered in three different ways: - as an :ref:`async ` function - as a :ref:`synchronous ` function - as a :ref:`threaded ` function .. _ls-handler-async: *Asynchronous* Functions (*Coroutines*) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *pygls* supports ``python 3.8+`` which has a keyword ``async`` to specify coroutines. The code snippet below shows how to register a command as a coroutine: .. code:: python @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_NON_BLOCKING) async def count_down_10_seconds_non_blocking(ls, *args): # Omitted Registering a *feature* as a coroutine is exactly the same. Coroutines are functions that are executed as tasks in *pygls*'s *event loop*. They should contain at least one *await* expression (see `awaitables `__ for details) which tells event loop to switch to another task while waiting. This allows *pygls* to listen for client requests in a *non blocking* way, while still only running in the *main* thread. Tasks can be canceled by the client if they didn't start executing (see `Cancellation Support `__). .. warning:: Using computation intensive operations will *block* the main thread and should be *avoided* inside coroutines. Take a look at `threaded functions <#threaded-functions>`__ for more details. .. _ls-handler-sync: *Synchronous* Functions ^^^^^^^^^^^^^^^^^^^^^^^ Synchronous functions are regular functions which *blocks* the *main* thread until they are executed. `Built-in features <#built-in-features>`__ are registered as regular functions to ensure correct state of language server initialization and workspace. The code snippet below shows how to register a command as a regular function: .. code:: python @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_BLOCKING) def count_down_10_seconds_blocking(ls, *args): # Omitted Registering *feature* as a regular function is exactly the same. .. warning:: Using computation intensive operations will *block* the main thread and should be *avoided* inside regular functions. Take a look at `threaded functions <#threaded-functions>`__ for more details. .. _ls-handler-thread: *Threaded* Functions ^^^^^^^^^^^^^^^^^^^^ *Threaded* functions are just regular functions, but marked with *pygls*'s ``thread`` decorator: .. code:: python # Decorator order is not important in this case @json_server.thread() @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_BLOCKING) def count_down_10_seconds_blocking(ls, *args): # Omitted *pygls* uses its own *thread pool* to execute above function in *daemon* thread and it is *lazy* initialized first time when function marked with ``thread`` decorator is fired. *Threaded* functions can be used to run blocking operations. If it has been a while or you are new to threading in Python, check out Python's ``multithreading`` and `GIL `__ before messing with threads. .. _passing-instance: Passing Language Server Instance ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Using language server methods inside registered features and commands are quite common. We recommend adding language server as a **first parameter** of a registered function. There are two ways of doing this: - **ls** (**l**\anguage **s**\erver) naming convention Add **ls** as first parameter of a function and *pygls* will automatically pass the language server instance. .. code-block:: python @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_BLOCKING) def count_down_10_seconds_blocking(ls, *args): # Omitted - add **type** to first parameter Add the **LanguageServer** class or any class derived from it as a type to first parameter of a function .. code-block:: python @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_BLOCKING) def count_down_10_seconds_blocking(ser: JsonLanguageServer, *args): # Omitted Using outer ``json_server`` instance inside registered function will make writing unit :ref:`tests ` more difficult. Communicating with the Client ----------------------------- .. important:: Most of the messages listed here cannot be sent until the LSP session has been initialized. See the section on the :lsp:`initiaiize` request in the specification for more details. In addition to responding to requests, there are a number of additional messages a server can send to the client. Configuration ~~~~~~~~~~~~~ The :lsp:`workspace/configuration` request is sent from the server to the client in order to fetch configuration settings from the client. Depending on how the handler is registered (see :ref:`here `) you can use the :meth:`~pygls.server.LanguageServer.get_configuration` or :meth:`~pygls.server.LanguageServer.get_configuration_async` methods to request configuration from the client: - *asynchronous* functions (*coroutines*) .. code:: python # await keyword tells event loop to switch to another task until notification is received config = await ls.get_configuration( WorkspaceConfigurationParams( items=[ ConfigurationItem(scope_uri='doc_uri_here', section='section') ] ) ) - *synchronous* functions .. code:: python # callback is called when notification is received def callback(config): # Omitted params = WorkspaceConfigurationParams( items=[ ConfigurationItem(scope_uri='doc_uri_here', section='section') ] ) config = ls.get_configuration(params, callback) - *threaded* functions .. code:: python # .result() will block the thread config = ls.get_configuration( WorkspaceConfigurationParams( items=[ ConfigurationItem(scope_uri='doc_uri_here', section='section') ] ) ).result() Publish Diagnostics ~~~~~~~~~~~~~~~~~~~ :lsp:`textDocument/publishDiagnostics` notifications are sent from the server to the client to highlight errors or potential issues. e.g. syntax errors or unused variables. Usually this notification is sent after document is opened, or on document content change: .. code:: python @json_server.feature(TEXT_DOCUMENT_DID_OPEN) async def did_open(ls, params: DidOpenTextDocumentParams): """Text document did open notification.""" ls.show_message("Text Document Did Open") ls.show_message_log("Validating json...") # Get document from workspace text_doc = ls.workspace.get_text_document(params.text_document.uri) diagnostic = Diagnostic( range=Range( start=Position(line-1, col-1), end=Position(line-1, col) ), message="Custom validation message", source="Json Server" ) # Send diagnostics ls.publish_diagnostics(text_doc.uri, [diagnostic]) Show Message ~~~~~~~~~~~~ :lsp:`window/showMessage` is a notification that is sent from the server to the client to display a prominant text message. e.g. VSCode will render this as a notification popup .. code:: python @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_NON_BLOCKING) async def count_down_10_seconds_non_blocking(ls, *args): for i in range(10): # Sends message notification to the client ls.show_message(f"Counting down... {10 - i}") await asyncio.sleep(1) Show Message Log ~~~~~~~~~~~~~~~~ :lsp:`window/logMessage` is a notification that is sent from the server to the client to display a discrete text message. e.g. VSCode will display the message in an :guilabel:`Output` channel. .. code:: python @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_NON_BLOCKING) async def count_down_10_seconds_non_blocking(ls, *args): for i in range(10): # Sends message log notification to the client ls.show_message_log(f"Counting down... {10 - i}") await asyncio.sleep(1) Workspace Edits ~~~~~~~~~~~~~~~ The :lsp:`workspace/applyEdit` request allows your language server to ask the client to modify particular documents in the client's workspace. .. code:: python def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> ApplyWorkspaceEditResponse: # Omitted def apply_edit_async(self, edit: WorkspaceEdit, label: str = None) -> ApplyWorkspaceEditResponse: # Omitted Custom Notifications ~~~~~~~~~~~~~~~~~~~~ .. warning:: Custom notifications are not part of the LSP specification and dedicated support for your custom notification(s) will have to be added to each language client you intend to support. A custom notification can be sent to the client using the :meth:`~pygls.server.LanguageServer.send_notification` method .. code:: python server.send_notification('myCustomNotification', 'test data') The Workspace ------------- The :class:`~pygls.workspace.Workspace` is a python object that holds information about workspace folders, opened documents and is responsible for synchronising server side document state with that of the client. **Text Documents** The :class:`~pygls.workspace.TextDocument` class is how *pygls* represents a text document. Given a text document's uri the :meth:`~pygls.workspace.Workspace.get_text_document` method can be used to access the document itself: .. code:: python @json_server.feature(TEXT_DOCUMENT_DID_OPEN) async def did_open(ls, params: DidOpenTextDocumentParams): # Get document from workspace text_doc = ls.workspace.get_text_document(params.text_document.uri) **Notebook Documents** .. seealso:: See the section on :lsp:`notebookDocument/synchronization` in the specification for full details on how notebook documents are handled - A notebook's structure, metadata etc. is represented using the :class:`~lsprotocol.types.NotebookDocument` class from ``lsprotocol``. - The contents of a single notebook cell is represented using a standard :class:`~pygls.workspace.TextDocument` In order to receive notebook documents from the client, your language server must provide an instance of :class:`~lsprotocol.types.NotebookDocumentSyncOptions` which declares the kind of notebooks it is interested in .. code-block:: python server = LanguageServer( name="example-server", version="v0.1", notebook_document_sync=types.NotebookDocumentSyncOptions( notebook_selector=[ types.NotebookDocumentSyncOptionsNotebookSelectorType2( cells=[ types.NotebookDocumentSyncOptionsNotebookSelectorType2CellsType( language="python" ) ] ) ] ), ) To access the contents of a notebook cell you would call the workspace's :meth:`~pygls.workspace.Workspace.get_text_document` method as normal. .. code-block:: python cell_doc = ls.workspace.get_text_document(cell_uri) To access the notebook itself call the workspace's :meth:`~pygls.workspace.Workspace.get_notebook_document` method with either the uri of the notebook *or* the uri of any of its cells. .. code-block:: python notebook_doc = ls.workspace.get_notebook_document(notebook_uri=notebook_uri) # -- OR -- notebook_doc = ls.workspace.get_notebook_document(cell_uri=cell_uri) pygls-1.3.0/examples/000077500000000000000000000000001455570166200145025ustar00rootroot00000000000000pygls-1.3.0/examples/hello-world/000077500000000000000000000000001455570166200167325ustar00rootroot00000000000000pygls-1.3.0/examples/hello-world/README.md000066400000000000000000000053031455570166200202120ustar00rootroot00000000000000# Hello World Pygls Language Server This is the bare-minimum, working example of a Pygls-based Language Server. It is the same as that shown in the main README, it autocompletes `hello.` with the options, "world" and "friend". You will only need to have installed Pygls on your system. Eg; `pip install pygls`. Normally you will want to formally define `pygls` as a dependency of your Language Server, with something like [venv](https://docs.python.org/3/library/venv.html), [Poetry](https://python-poetry.org/), etc. # Editor Configurations
Neovim Lua (vanilla Neovim without `lspconfig`) Normally, once you have completed your own Language Server, you will want to submit it to the [LSP Config](https://github.com/neovim/nvim-lspconfig) repo, it is the defacto way to support Language Servers in the Neovim ecosystem. But before then you can just use something like this: ```lua vim.api.nvim_create_autocmd({ "BufEnter" }, { -- NB: You must remember to manually put the file extension pattern matchers for each LSP filetype pattern = { "*" }, callback = function() vim.lsp.start({ name = "hello-world-pygls-example", cmd = { "python path-to-hello-world-example/main.py" }, root_dir = vim.fs.dirname(vim.fs.find({ ".git" }, { upward = true })[1]) }) end, }) ```
Vim (`vim-lsp`) ```vim augroup HelloWorldPythonExample au! autocmd User lsp_setup call lsp#register_server({ \ 'name': 'hello-world-pygls-example', \ 'cmd': {server_info->['python', 'path-to-hello-world-example/main.py']}, \ 'allowlist': ['*'] \ }}) augroup END ```
Emacs (`lsp-mode`) Normally, once your Language Server is complete, you'll want to submit it to the [M-x Eglot](https://github.com/joaotavora/eglot) project, which will automatically set your server up. Until then, you can use: ``` (make-lsp-client :new-connection (lsp-stdio-connection `(,(executable-find "python") "path-to-hello-world-example/main.py")) :activation-fn (lsp-activate-on "*") :server-id 'hello-world-pygls-example'))) ```
Sublime ``` { "clients": { "pygls-hello-world-example": { "command": ["python", "path-to-hello-world-example/main.py"], "enabled": true, "selector": "source.python" } } } ```
VSCode VSCode is the most complex of the editors to setup. See the [json-vscode-extension](https://github.com/openlawlibrary/pygls/tree/master/examples/json-vscode-extension) for an idea of how to do it.
pygls-1.3.0/examples/hello-world/main.py000066400000000000000000000013071455570166200202310ustar00rootroot00000000000000from pygls.server import LanguageServer from lsprotocol.types import ( TEXT_DOCUMENT_COMPLETION, CompletionItem, CompletionList, CompletionParams, ) server = LanguageServer("example-server", "v0.1") @server.feature(TEXT_DOCUMENT_COMPLETION) def completions(params: CompletionParams): items = [] document = server.workspace.get_document(params.text_document.uri) current_line = document.lines[params.position.line].strip() if current_line.endswith("hello."): items = [ CompletionItem(label="world"), CompletionItem(label="friend"), ] return CompletionList( is_incomplete=False, items=items, ) server.start_io() pygls-1.3.0/examples/servers/000077500000000000000000000000001455570166200161735ustar00rootroot00000000000000pygls-1.3.0/examples/servers/.vscode/000077500000000000000000000000001455570166200175345ustar00rootroot00000000000000pygls-1.3.0/examples/servers/.vscode/launch.json000066400000000000000000000010151455570166200216760ustar00rootroot00000000000000{ "configurations": [ { "name": "pygls: Debug Server", "type": "python", "request": "attach", "connect": { "host": "${config:pygls.server.debugHost}", "port": "${config:pygls.server.debugPort}" }, "pathMappings": [ { "localRoot": "${workspaceFolder}", "remoteRoot": "." } ], "justMyCode": false } ] } pygls-1.3.0/examples/servers/.vscode/settings.json000066400000000000000000000012351455570166200222700ustar00rootroot00000000000000{ // Uncomment to override Python interpreter used. // "pygls.server.pythonPath": "/path/to/python", "pygls.server.debug": false, // "pygls.server.debugHost": "localhost", // "pygls.server.debugPort": 5678, "pygls.server.launchScript": "json_server.py", "pygls.trace.server": "off", "pygls.client.documentSelector": [ { "scheme": "file", "language": "json" } // Uncomment to use code_actions or inlay_hints servers // { // "scheme": "file", // "language": "plaintext" // } ], // "pygls.jsonServer.exampleConfiguration": "some value here", } pygls-1.3.0/examples/servers/code_actions.py000066400000000000000000000054231455570166200212030ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import re from pygls.server import LanguageServer from lsprotocol.types import ( TEXT_DOCUMENT_CODE_ACTION, CodeAction, CodeActionKind, CodeActionOptions, CodeActionParams, Position, Range, TextEdit, WorkspaceEdit, ) ADDITION = re.compile(r"^\s*(\d+)\s*\+\s*(\d+)\s*=(?=\s*$)") server = LanguageServer("code-action-server", "v0.1") @server.feature( TEXT_DOCUMENT_CODE_ACTION, CodeActionOptions(code_action_kinds=[CodeActionKind.QuickFix]), ) def code_actions(params: CodeActionParams): items = [] document_uri = params.text_document.uri document = server.workspace.get_document(document_uri) start_line = params.range.start.line end_line = params.range.end.line lines = document.lines[start_line : end_line + 1] for idx, line in enumerate(lines): match = ADDITION.match(line) if match is not None: range_ = Range( start=Position(line=start_line + idx, character=0), end=Position(line=start_line + idx, character=len(line) - 1), ) left = int(match.group(1)) right = int(match.group(2)) answer = left + right text_edit = TextEdit(range=range_, new_text=f"{line.strip()} {answer}!") action = CodeAction( title=f"Evaluate '{match.group(0)}'", kind=CodeActionKind.QuickFix, edit=WorkspaceEdit(changes={document_uri: [text_edit]}), ) items.append(action) return items if __name__ == "__main__": server.start_io() pygls-1.3.0/examples/servers/inlay_hints.py000066400000000000000000000077111455570166200210740ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import re from typing import Optional from lsprotocol import types from pygls.server import LanguageServer NUMBER = re.compile(r"\d+") COMMENT = re.compile(r"^#$") server = LanguageServer( name="inlay-hint-server", version="v0.1", notebook_document_sync=types.NotebookDocumentSyncOptions( notebook_selector=[ types.NotebookDocumentSyncOptionsNotebookSelectorType2( cells=[ types.NotebookDocumentSyncOptionsNotebookSelectorType2CellsType( language="python" ) ] ) ] ), ) def parse_int(chars: str) -> Optional[int]: try: return int(chars) except Exception: return None @server.feature(types.TEXT_DOCUMENT_INLAY_HINT) def inlay_hints(params: types.InlayHintParams): items = [] document_uri = params.text_document.uri document = server.workspace.get_text_document(document_uri) start_line = params.range.start.line end_line = params.range.end.line lines = document.lines[start_line : end_line + 1] for lineno, line in enumerate(lines): match = COMMENT.match(line) if match is not None: nb = server.workspace.get_notebook_document(cell_uri=document_uri) if nb is not None: idx = 0 for idx, cell in enumerate(nb.cells): if cell.document == document_uri: break items.append( types.InlayHint( label=f"notebook: {nb.uri}, cell {idx+1}", kind=types.InlayHintKind.Type, padding_left=False, padding_right=True, position=types.Position(line=lineno, character=match.end()), ) ) for match in NUMBER.finditer(line): if not match: continue number = parse_int(match.group(0)) if number is None: continue binary_num = bin(number).split("b")[1] items.append( types.InlayHint( label=f":{binary_num}", kind=types.InlayHintKind.Type, padding_left=False, padding_right=True, position=types.Position(line=lineno, character=match.end()), ) ) return items @server.feature(types.INLAY_HINT_RESOLVE) def inlay_hint_resolve(hint: types.InlayHint): try: n = int(hint.label[1:], 2) hint.tooltip = f"Binary representation of the number: {n}" except Exception: pass return hint if __name__ == "__main__": server.start_io() pygls-1.3.0/examples/servers/json_server.py000066400000000000000000000313121455570166200211040ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import argparse import asyncio import json import re import time import uuid from json import JSONDecodeError from typing import Optional from lsprotocol import types as lsp from pygls.server import LanguageServer COUNT_DOWN_START_IN_SECONDS = 10 COUNT_DOWN_SLEEP_IN_SECONDS = 1 class JsonLanguageServer(LanguageServer): CMD_COUNT_DOWN_BLOCKING = "countDownBlocking" CMD_COUNT_DOWN_NON_BLOCKING = "countDownNonBlocking" CMD_PROGRESS = "progress" CMD_REGISTER_COMPLETIONS = "registerCompletions" CMD_SHOW_CONFIGURATION_ASYNC = "showConfigurationAsync" CMD_SHOW_CONFIGURATION_CALLBACK = "showConfigurationCallback" CMD_SHOW_CONFIGURATION_THREAD = "showConfigurationThread" CMD_UNREGISTER_COMPLETIONS = "unregisterCompletions" CONFIGURATION_SECTION = "pygls.jsonServer" def __init__(self, *args): super().__init__(*args) json_server = JsonLanguageServer("pygls-json-example", "v0.1") def _validate(ls, params): ls.show_message_log("Validating json...") text_doc = ls.workspace.get_document(params.text_document.uri) source = text_doc.source diagnostics = _validate_json(source) if source else [] ls.publish_diagnostics(text_doc.uri, diagnostics) def _validate_json(source): """Validates json file.""" diagnostics = [] try: json.loads(source) except JSONDecodeError as err: msg = err.msg col = err.colno line = err.lineno d = lsp.Diagnostic( range=lsp.Range( start=lsp.Position(line=line - 1, character=col - 1), end=lsp.Position(line=line - 1, character=col), ), message=msg, source=type(json_server).__name__, ) diagnostics.append(d) return diagnostics @json_server.feature( lsp.TEXT_DOCUMENT_DIAGNOSTIC, lsp.DiagnosticOptions( identifier="jsonServer", inter_file_dependencies=True, workspace_diagnostics=True, ), ) def text_document_diagnostic( params: lsp.DocumentDiagnosticParams, ) -> lsp.DocumentDiagnosticReport: """Returns diagnostic report.""" document = json_server.workspace.get_document(params.text_document.uri) return lsp.RelatedFullDocumentDiagnosticReport( items=_validate_json(document.source), kind=lsp.DocumentDiagnosticReportKind.Full, ) @json_server.feature(lsp.WORKSPACE_DIAGNOSTIC) def workspace_diagnostic( params: lsp.WorkspaceDiagnosticParams, ) -> lsp.WorkspaceDiagnosticReport: """Returns diagnostic report.""" documents = json_server.workspace.text_documents.keys() if len(documents) == 0: items = [] else: first = list(documents)[0] document = json_server.workspace.get_document(first) items = [ lsp.WorkspaceFullDocumentDiagnosticReport( uri=document.uri, version=document.version, items=_validate_json(document.source), kind=lsp.DocumentDiagnosticReportKind.Full, ) ] return lsp.WorkspaceDiagnosticReport(items=items) @json_server.feature( lsp.TEXT_DOCUMENT_COMPLETION, lsp.CompletionOptions(trigger_characters=[","], all_commit_characters=[":"]), ) def completions(params: Optional[lsp.CompletionParams] = None) -> lsp.CompletionList: """Returns completion items.""" return lsp.CompletionList( is_incomplete=False, items=[ lsp.CompletionItem(label='"'), lsp.CompletionItem(label="["), lsp.CompletionItem(label="]"), lsp.CompletionItem(label="{"), lsp.CompletionItem(label="}"), ], ) @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_BLOCKING) def count_down_10_seconds_blocking(ls, *args): """Starts counting down and showing message synchronously. It will `block` the main thread, which can be tested by trying to show completion items. """ for i in range(COUNT_DOWN_START_IN_SECONDS): ls.show_message(f"Counting down... {COUNT_DOWN_START_IN_SECONDS - i}") time.sleep(COUNT_DOWN_SLEEP_IN_SECONDS) @json_server.command(JsonLanguageServer.CMD_COUNT_DOWN_NON_BLOCKING) async def count_down_10_seconds_non_blocking(ls, *args): """Starts counting down and showing message asynchronously. It won't `block` the main thread, which can be tested by trying to show completion items. """ for i in range(COUNT_DOWN_START_IN_SECONDS): ls.show_message(f"Counting down... {COUNT_DOWN_START_IN_SECONDS - i}") await asyncio.sleep(COUNT_DOWN_SLEEP_IN_SECONDS) @json_server.feature(lsp.TEXT_DOCUMENT_DID_CHANGE) def did_change(ls, params: lsp.DidChangeTextDocumentParams): """Text document did change notification.""" _validate(ls, params) @json_server.feature(lsp.TEXT_DOCUMENT_DID_CLOSE) def did_close(server: JsonLanguageServer, params: lsp.DidCloseTextDocumentParams): """Text document did close notification.""" server.show_message("Text Document Did Close") @json_server.feature(lsp.TEXT_DOCUMENT_DID_OPEN) async def did_open(ls, params: lsp.DidOpenTextDocumentParams): """Text document did open notification.""" ls.show_message("Text Document Did Open") _validate(ls, params) @json_server.feature( lsp.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, lsp.SemanticTokensLegend(token_types=["operator"], token_modifiers=[]), ) def semantic_tokens(ls: JsonLanguageServer, params: lsp.SemanticTokensParams): """See https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens for details on how semantic tokens are encoded.""" TOKENS = re.compile('".*"(?=:)') uri = params.text_document.uri doc = ls.workspace.get_document(uri) last_line = 0 last_start = 0 data = [] for lineno, line in enumerate(doc.lines): last_start = 0 for match in TOKENS.finditer(line): start, end = match.span() data += [(lineno - last_line), (start - last_start), (end - start), 0, 0] last_line = lineno last_start = start return lsp.SemanticTokens(data=data) @json_server.feature(lsp.TEXT_DOCUMENT_INLINE_VALUE) def inline_value(params: lsp.InlineValueParams): """Returns inline value.""" return [lsp.InlineValueText(range=params.range, text="Inline value")] @json_server.command(JsonLanguageServer.CMD_PROGRESS) async def progress(ls: JsonLanguageServer, *args): """Create and start the progress on the client.""" token = str(uuid.uuid4()) # Create await ls.progress.create_async(token) # Begin ls.progress.begin( token, lsp.WorkDoneProgressBegin(title="Indexing", percentage=0, cancellable=True), ) # Report for i in range(1, 10): # Check for cancellation from client if ls.progress.tokens[token].cancelled(): # ... and stop the computation if client cancelled return ls.progress.report( token, lsp.WorkDoneProgressReport(message=f"{i * 10}%", percentage=i * 10), ) await asyncio.sleep(2) # End ls.progress.end(token, lsp.WorkDoneProgressEnd(message="Finished")) @json_server.command(JsonLanguageServer.CMD_REGISTER_COMPLETIONS) async def register_completions(ls: JsonLanguageServer, *args): """Register completions method on the client.""" params = lsp.RegistrationParams( registrations=[ lsp.Registration( id=str(uuid.uuid4()), method=lsp.TEXT_DOCUMENT_COMPLETION, register_options={"triggerCharacters": "[':']"}, ) ] ) response = await ls.register_capability_async(params) if response is None: ls.show_message("Successfully registered completions method") else: ls.show_message( "Error happened during completions registration.", lsp.MessageType.Error ) @json_server.command(JsonLanguageServer.CMD_SHOW_CONFIGURATION_ASYNC) async def show_configuration_async(ls: JsonLanguageServer, *args): """Gets exampleConfiguration from the client settings using coroutines.""" try: config = await ls.get_configuration_async( lsp.WorkspaceConfigurationParams( items=[ lsp.ConfigurationItem( scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION ) ] ) ) example_config = config[0].get("exampleConfiguration") ls.show_message(f"jsonServer.exampleConfiguration value: {example_config}") except Exception as e: ls.show_message_log(f"Error ocurred: {e}") @json_server.command(JsonLanguageServer.CMD_SHOW_CONFIGURATION_CALLBACK) def show_configuration_callback(ls: JsonLanguageServer, *args): """Gets exampleConfiguration from the client settings using callback.""" def _config_callback(config): try: example_config = config[0].get("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( lsp.WorkspaceConfigurationParams( items=[ lsp.ConfigurationItem( scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION ) ] ), _config_callback, ) @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 thread pool.""" try: config = ls.get_configuration( lsp.WorkspaceConfigurationParams( items=[ lsp.ConfigurationItem( scope_uri="", section=JsonLanguageServer.CONFIGURATION_SECTION ) ] ) ).result(2) example_config = config[0].get("exampleConfiguration") ls.show_message(f"jsonServer.exampleConfiguration value: {example_config}") except Exception as e: ls.show_message_log(f"Error ocurred: {e}") @json_server.command(JsonLanguageServer.CMD_UNREGISTER_COMPLETIONS) async def unregister_completions(ls: JsonLanguageServer, *args): """Unregister completions method on the client.""" params = lsp.UnregistrationParams( unregisterations=[ lsp.Unregistration( id=str(uuid.uuid4()), method=lsp.TEXT_DOCUMENT_COMPLETION ) ] ) response = await ls.unregister_capability_async(params) if response is None: ls.show_message("Successfully unregistered completions method") else: ls.show_message( "Error happened during completions unregistration.", lsp.MessageType.Error ) def add_arguments(parser): parser.description = "simple json server example" parser.add_argument("--tcp", action="store_true", help="Use TCP server") parser.add_argument("--ws", action="store_true", help="Use WebSocket server") parser.add_argument("--host", default="127.0.0.1", help="Bind to this address") parser.add_argument("--port", type=int, default=2087, help="Bind to this port") def main(): parser = argparse.ArgumentParser() add_arguments(parser) args = parser.parse_args() if args.tcp: json_server.start_tcp(args.host, args.port) elif args.ws: json_server.start_ws(args.host, args.port) else: json_server.start_io() if __name__ == "__main__": main() pygls-1.3.0/examples/servers/workspace/000077500000000000000000000000001455570166200201715ustar00rootroot00000000000000pygls-1.3.0/examples/servers/workspace/Untitled-1.ipynb000066400000000000000000000016561455570166200231720ustar00rootroot00000000000000{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "12\n", "#" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "mykey": 3 }, "outputs": [], "source": [ "#" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.11.4" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 } pygls-1.3.0/examples/servers/workspace/sums.txt000066400000000000000000000000341455570166200217160ustar00rootroot000000000000001 + 1 = 2 + 3 = 6 + 6 = pygls-1.3.0/examples/servers/workspace/test.json000066400000000000000000000000271455570166200220420ustar00rootroot00000000000000{ "key": "value" } pygls-1.3.0/examples/vscode-playground/000077500000000000000000000000001455570166200201475ustar00rootroot00000000000000pygls-1.3.0/examples/vscode-playground/.eslintrc.yml000066400000000000000000000003651455570166200225770ustar00rootroot00000000000000env: es2021: true node: true extends: - 'eslint:recommended' - 'plugin:@typescript-eslint/recommended' parser: '@typescript-eslint/parser' parserOptions: ecmaVersion: 12 sourceType: module plugins: - '@typescript-eslint' rules: {} pygls-1.3.0/examples/vscode-playground/.gitignore000066400000000000000000000001061455570166200221340ustar00rootroot00000000000000out node_modules client/server .vscode-test .vscode/settings.json env pygls-1.3.0/examples/vscode-playground/.vscode/000077500000000000000000000000001455570166200215105ustar00rootroot00000000000000pygls-1.3.0/examples/vscode-playground/.vscode/launch.json000066400000000000000000000012561455570166200236610ustar00rootroot00000000000000// A launch configuration that compiles the extension and then opens it inside a new window { "version": "0.2.0", "configurations": [ { "name": "Launch Client", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "args": [ "--extensionDevelopmentPath=${workspaceRoot}", "--folder-uri=${workspaceRoot}/../servers", ], "outFiles": [ "${workspaceRoot}/out/**/*.js" ], "preLaunchTask": { "type": "npm", "script": "watch" }, }, ], } pygls-1.3.0/examples/vscode-playground/.vscode/tasks.json000066400000000000000000000012671455570166200235360ustar00rootroot00000000000000{ "version": "2.0.0", "tasks": [ { "type": "npm", "script": "compile", "group": "build", "presentation": { "panel": "dedicated", "reveal": "never" }, "problemMatcher": ["$tsc"] }, { "type": "npm", "script": "watch", "isBackground": true, "group": { "kind": "build", "isDefault": true }, "presentation": { "panel": "dedicated", "reveal": "never" }, "problemMatcher": ["$tsc-watch"] } ] } pygls-1.3.0/examples/vscode-playground/.vscodeignore000066400000000000000000000001701455570166200226350ustar00rootroot00000000000000.vscode .gitignore client/out/*.map client/src/ tsconfig.json tslint.json package.json package-lock.json .pytest_cache pygls-1.3.0/examples/vscode-playground/LICENSE.txt000066400000000000000000000261471455570166200220040ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright (c) Open Law Library. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. pygls-1.3.0/examples/vscode-playground/README.md000066400000000000000000000061701455570166200214320ustar00rootroot00000000000000# Pygls Playground ![Screenshot of the vscode-playground extension in action](https://user-images.githubusercontent.com/2675694/260591942-b7001a7b-3081-439d-b702-5f8a489856db.png) This VSCode extension aims to serve two purposes. - Provide an environment in which you can easily experiment with the pygls framework by trying some of our example servers - or by writing your own - Provide a minimal example of what it takes to integrate a pygls powered language server into VSCode. For an example of a more complete VSCode client, including details on how to bundle your Python code with the VSCode extension itself you may also be interested in Microsoft's [template extension for Python tools](https://github.com/microsoft/vscode-python-tools-extension-template). ## Setup ### Install Server Dependencies Open a terminal in the repository's root directory 1. Create a virtual environment ``` python -m venv env ``` 1. Install pygls ``` python -m pip install -e . ``` ### Install Client Dependencies Open terminal in the same directory as this file and execute following commands: 1. Install node dependencies ``` npm install ``` 1. Compile the extension ``` npm run compile ``` Alternatively you can run `npm run watch` if you are going to be actively working on the extension itself. ### Run Extension 1. Open this directory in VS Code 1. The playground relies on the [Python extension for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-python.python) for choosing the appropriate Python environment in which to run the example language servers. If you haven't already, you will need to install it and reload the window. 1. Open the Run and Debug view (`ctrl + shift + D`) 1. Select `Launch Client` and press `F5`, this will open a second VSCode window with the `vscode-playground` extension enabled. 1. You will need to make sure that VSCode is using a virtual environment that contains an installation of `pygls`. The `Python: Select Interpreter` command can be used to pick the correct one. Alternatively, you can set the `pygls.server.pythonPath` option in the `.vscode/settings.json` file #### Selecting the document language The default settings for the `pygls-playground` VSCode extension are configured for the `json_server.py` example. In particular the server will only be used for `.json` files. The `code_actions.py` example is intended to be used with text files (e.g. the provided `sums.txt` file). To use the server with text files change the **Pygls > Client: Document Selector** setting to the following: ``` "pygls.client.documentSelector": [ { "scheme": "file", "language": "plaintext" }, ], ``` You can find the full list of known language identifiers [here](https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers). #### Debugging the server To debug the language server set the `pygls.server.debug` option to `true`. The server should be restarted and the debugger connect automatically. You can control the host and port that the debugger uses through the `pygls.server.debugHost` and `pygls.server.debugPort` options. pygls-1.3.0/examples/vscode-playground/package-lock.json000066400000000000000000003062461455570166200233760ustar00rootroot00000000000000{ "name": "pygls-playground", "version": "1.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pygls-playground", "version": "1.0.2", "license": "Apache-2.0", "dependencies": { "@vscode/python-extension": "^1.0.4", "semver": "^7.5.4", "vscode-languageclient": "^8.1.0" }, "devDependencies": { "@types/node": "^16.11.6", "@types/semver": "^7.5.0", "@types/vscode": "^1.78.0", "@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/parser": "^5.3.0", "eslint": "^8.2.0", "typescript": "^5.1.0" }, "engines": { "node": ">=16.17.1", "vscode": "^1.78.0" } }, "node_modules/@eslint/eslintrc": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.0.0", "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/@humanwhocodes/config-array": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.0", "debug": "^4.1.1", "minimatch": "^3.0.4" }, "engines": { "node": ">=10.10.0" } }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { "node": ">= 8" } }, "node_modules/@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "node_modules/@types/node": { "version": "16.11.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", "dev": true }, "node_modules/@types/semver": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "node_modules/@types/vscode": { "version": "1.79.1", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.1.tgz", "integrity": "sha512-Ikwc4YbHABzqthrWfeAvItaAIfX9mdjMWxqNgTpGjhgOu0TMRq9LzyZ2yBK0JhYqoSjEubEPawf6zJgnl6Egtw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.0.tgz", "integrity": "sha512-ARUEJHJrq85aaiCqez7SANeahDsJTD3AEua34EoQN9pHS6S5Bq9emcIaGGySt/4X2zSi+vF5hAH52sEen7IO7g==", "dev": true, "dependencies": { "@typescript-eslint/experimental-utils": "5.3.0", "@typescript-eslint/scope-manager": "5.3.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", "regexpp": "^3.2.0", "semver": "^7.3.5", "tsutils": "^3.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "@typescript-eslint/parser": "^5.0.0", "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/experimental-utils": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.0.tgz", "integrity": "sha512-NFVxYTjKj69qB0FM+piah1x3G/63WB8vCBMnlnEHUsiLzXSTWb9FmFn36FD9Zb4APKBLY3xRArOGSMQkuzTF1w==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@typescript-eslint/scope-manager": "5.3.0", "@typescript-eslint/types": "5.3.0", "@typescript-eslint/typescript-estree": "5.3.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "*" } }, "node_modules/@typescript-eslint/parser": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.0.tgz", "integrity": "sha512-rKu/yAReip7ovx8UwOAszJVO5MgBquo8WjIQcp1gx4pYQCwYzag+I5nVNHO4MqyMkAo0gWt2gWUi+36gWAVKcw==", "dev": true, "dependencies": { "@typescript-eslint/scope-manager": "5.3.0", "@typescript-eslint/types": "5.3.0", "@typescript-eslint/typescript-estree": "5.3.0", "debug": "^4.3.2" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.0.tgz", "integrity": "sha512-22Uic9oRlTsPppy5Tcwfj+QET5RWEnZ5414Prby465XxQrQFZ6nnm5KnXgnsAJefG4hEgMnaxTB3kNEyjdjj6A==", "dev": true, "dependencies": { "@typescript-eslint/types": "5.3.0", "@typescript-eslint/visitor-keys": "5.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/types": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.0.tgz", "integrity": "sha512-fce5pG41/w8O6ahQEhXmMV+xuh4+GayzqEogN24EK+vECA3I6pUwKuLi5QbXO721EMitpQne5VKXofPonYlAQg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/typescript-estree": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.0.tgz", "integrity": "sha512-FJ0nqcaUOpn/6Z4Jwbtf+o0valjBLkqc3MWkMvrhA2TvzFXtcclIM8F4MBEmYa2kgcI8EZeSAzwoSrIC8JYkug==", "dev": true, "dependencies": { "@typescript-eslint/types": "5.3.0", "@typescript-eslint/visitor-keys": "5.3.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", "semver": "^7.3.5", "tsutils": "^3.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.0.tgz", "integrity": "sha512-oVIAfIQuq0x2TFDNLVavUn548WL+7hdhxYn+9j3YdJJXB7mH9dAmZNJsPDa7Jc+B9WGqoiex7GUDbyMxV0a/aw==", "dev": true, "dependencies": { "@typescript-eslint/types": "5.3.0", "eslint-visitor-keys": "^3.0.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@vscode/python-extension": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@vscode/python-extension/-/python-extension-1.0.4.tgz", "integrity": "sha512-+m9VOUqv5TXZD52Ad8FjGbYGch7VqLAIys3NyVhgU6eSxmXVcRgqeon5ee224tOkTGtRQHdH5kDCa1Va/6LwjQ==", "engines": { "node": ">=16.17.1", "vscode": "^1.78.0" } }, "node_modules/acorn": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true, "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { "fill-range": "^7.0.1" }, "engines": { "node": ">=8" } }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { "path-type": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { "esutils": "^2.0.2" }, "engines": { "node": ">=6.0.0" } }, "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "dependencies": { "ansi-colors": "^4.1.1" }, "engines": { "node": ">=8.6" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.4", "@humanwhocodes/config-array": "^0.6.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^6.0.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.0.0", "espree": "^9.0.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "progress": "^2.0.0", "regexpp": "^3.2.0", "semver": "^7.2.1", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" }, "engines": { "node": ">=8.0.0" } }, "node_modules/eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "dependencies": { "eslint-visitor-keys": "^2.0.0" }, "engines": { "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { "eslint": ">=5" } }, "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/eslint-visitor-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/eslint/node_modules/eslint-scope": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", "integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/eslint/node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" } }, "node_modules/eslint/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/espree": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", "dev": true, "dependencies": { "acorn": "^8.5.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^3.0.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "dependencies": { "estraverse": "^5.1.0" }, "engines": { "node": ">=0.10" } }, "node_modules/esquery/node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" } }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { "estraverse": "^5.2.0" }, "engines": { "node": ">=4.0" } }, "node_modules/esrecurse/node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "engines": { "node": ">=4.0" } }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "node_modules/fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" }, "engines": { "node": ">=8" } }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { "flat-cache": "^3.0.4" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "dependencies": { "flatted": "^3.1.0", "rimraf": "^3.0.2" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { "is-glob": "^4.0.3" }, "engines": { "node": ">=10.13.0" } }, "node_modules/globals": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/ignore": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true, "engines": { "node": ">=0.8.19" } }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "engines": { "node": ">=0.12.0" } }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "dependencies": { "braces": "^3.0.1", "picomatch": "^2.2.3" }, "engines": { "node": ">=8.6" } }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "dependencies": { "wrappy": "1" } }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.3" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { "callsites": "^3.0.0" }, "engines": { "node": ">=6" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true, "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "engines": { "node": ">= 0.8.0" } }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "engines": { "node": ">=0.4.0" } }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/mysticatea" } }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "dependencies": { "queue-microtask": "^1.2.2" } }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "dependencies": { "tslib": "^1.8.1" }, "engines": { "node": ">= 6" }, "peerDependencies": { "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typescript": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=14.17" } }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "dependencies": { "punycode": "^2.1.0" } }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "node_modules/vscode-jsonrpc": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz", "integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==", "dependencies": { "minimatch": "^5.1.0", "semver": "^7.3.7", "vscode-languageserver-protocol": "3.17.3" }, "engines": { "vscode": "^1.67.0" } }, "node_modules/vscode-languageclient/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/vscode-languageclient/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, "node_modules/vscode-languageserver-protocol": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", "dependencies": { "vscode-jsonrpc": "8.1.0", "vscode-languageserver-types": "3.17.3" } }, "node_modules/vscode-languageserver-types": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/word-wrap": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } }, "dependencies": { "@eslint/eslintrc": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.4.tgz", "integrity": "sha512-h8Vx6MdxwWI2WM8/zREHMoqdgLNXEL4QX3MWSVMdyNJGvXVOs+6lp+m2hc3FnuMHDc4poxFNI20vCk0OmI4G0Q==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.0.0", "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "dependencies": { "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true } } }, "@humanwhocodes/config-array": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.0", "debug": "^4.1.1", "minimatch": "^3.0.4" } }, "@humanwhocodes/object-schema": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "@types/json-schema": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "@types/node": { "version": "16.11.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", "dev": true }, "@types/semver": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "@types/vscode": { "version": "1.79.1", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.1.tgz", "integrity": "sha512-Ikwc4YbHABzqthrWfeAvItaAIfX9mdjMWxqNgTpGjhgOu0TMRq9LzyZ2yBK0JhYqoSjEubEPawf6zJgnl6Egtw==", "dev": true }, "@typescript-eslint/eslint-plugin": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.3.0.tgz", "integrity": "sha512-ARUEJHJrq85aaiCqez7SANeahDsJTD3AEua34EoQN9pHS6S5Bq9emcIaGGySt/4X2zSi+vF5hAH52sEen7IO7g==", "dev": true, "requires": { "@typescript-eslint/experimental-utils": "5.3.0", "@typescript-eslint/scope-manager": "5.3.0", "debug": "^4.3.2", "functional-red-black-tree": "^1.0.1", "ignore": "^5.1.8", "regexpp": "^3.2.0", "semver": "^7.3.5", "tsutils": "^3.21.0" } }, "@typescript-eslint/experimental-utils": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.3.0.tgz", "integrity": "sha512-NFVxYTjKj69qB0FM+piah1x3G/63WB8vCBMnlnEHUsiLzXSTWb9FmFn36FD9Zb4APKBLY3xRArOGSMQkuzTF1w==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", "@typescript-eslint/scope-manager": "5.3.0", "@typescript-eslint/types": "5.3.0", "@typescript-eslint/typescript-estree": "5.3.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.3.0.tgz", "integrity": "sha512-rKu/yAReip7ovx8UwOAszJVO5MgBquo8WjIQcp1gx4pYQCwYzag+I5nVNHO4MqyMkAo0gWt2gWUi+36gWAVKcw==", "dev": true, "requires": { "@typescript-eslint/scope-manager": "5.3.0", "@typescript-eslint/types": "5.3.0", "@typescript-eslint/typescript-estree": "5.3.0", "debug": "^4.3.2" } }, "@typescript-eslint/scope-manager": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.3.0.tgz", "integrity": "sha512-22Uic9oRlTsPppy5Tcwfj+QET5RWEnZ5414Prby465XxQrQFZ6nnm5KnXgnsAJefG4hEgMnaxTB3kNEyjdjj6A==", "dev": true, "requires": { "@typescript-eslint/types": "5.3.0", "@typescript-eslint/visitor-keys": "5.3.0" } }, "@typescript-eslint/types": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.3.0.tgz", "integrity": "sha512-fce5pG41/w8O6ahQEhXmMV+xuh4+GayzqEogN24EK+vECA3I6pUwKuLi5QbXO721EMitpQne5VKXofPonYlAQg==", "dev": true }, "@typescript-eslint/typescript-estree": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.3.0.tgz", "integrity": "sha512-FJ0nqcaUOpn/6Z4Jwbtf+o0valjBLkqc3MWkMvrhA2TvzFXtcclIM8F4MBEmYa2kgcI8EZeSAzwoSrIC8JYkug==", "dev": true, "requires": { "@typescript-eslint/types": "5.3.0", "@typescript-eslint/visitor-keys": "5.3.0", "debug": "^4.3.2", "globby": "^11.0.4", "is-glob": "^4.0.3", "semver": "^7.3.5", "tsutils": "^3.21.0" } }, "@typescript-eslint/visitor-keys": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.3.0.tgz", "integrity": "sha512-oVIAfIQuq0x2TFDNLVavUn548WL+7hdhxYn+9j3YdJJXB7mH9dAmZNJsPDa7Jc+B9WGqoiex7GUDbyMxV0a/aw==", "dev": true, "requires": { "@typescript-eslint/types": "5.3.0", "eslint-visitor-keys": "^3.0.0" } }, "@vscode/python-extension": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@vscode/python-extension/-/python-extension-1.0.4.tgz", "integrity": "sha512-+m9VOUqv5TXZD52Ad8FjGbYGch7VqLAIys3NyVhgU6eSxmXVcRgqeon5ee224tOkTGtRQHdH5kDCa1Va/6LwjQ==" }, "acorn": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" } }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { "fill-range": "^7.0.1" } }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" } }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { "path-type": "^4.0.0" } }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { "esutils": "^2.0.2" } }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "requires": { "ansi-colors": "^4.1.1" } }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "eslint": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.4", "@humanwhocodes/config-array": "^0.6.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^6.0.0", "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.0.0", "espree": "^9.0.0", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", "progress": "^2.0.0", "regexpp": "^3.2.0", "semver": "^7.2.1", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "dependencies": { "eslint-scope": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", "integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==", "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true } } }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "eslint-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { "eslint-visitor-keys": "^2.0.0" }, "dependencies": { "eslint-visitor-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true } } }, "eslint-visitor-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", "dev": true }, "espree": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", "dev": true, "requires": { "acorn": "^8.5.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^3.0.0" } }, "esquery": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { "estraverse": "^5.1.0" }, "dependencies": { "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } }, "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { "estraverse": "^5.2.0" }, "dependencies": { "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" }, "dependencies": { "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" } } } }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" } }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { "flat-cache": "^3.0.4" } }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { "to-regex-range": "^5.0.1" } }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { "flatted": "^3.1.0", "rimraf": "^3.0.2" } }, "flatted": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { "is-glob": "^4.0.3" } }, "globals": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, "globby": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.1.1", "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" } }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "ignore": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", "dev": true }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" } }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" } }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "requires": { "yallist": "^4.0.0" } }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, "micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", "picomatch": "^2.2.3" } }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" } }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "requires": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.3" } }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { "callsites": "^3.0.0" } }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { "queue-microtask": "^1.2.2" } }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { "shebang-regex": "^3.0.0" } }, "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" } }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" } }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" } }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "requires": { "tslib": "^1.8.1" } }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { "prelude-ls": "^1.2.1" } }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, "typescript": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" } }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "vscode-jsonrpc": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==" }, "vscode-languageclient": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz", "integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==", "requires": { "minimatch": "^5.1.0", "semver": "^7.3.7", "vscode-languageserver-protocol": "3.17.3" }, "dependencies": { "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "requires": { "balanced-match": "^1.0.0" } }, "minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "requires": { "brace-expansion": "^2.0.1" } } } }, "vscode-languageserver-protocol": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", "requires": { "vscode-jsonrpc": "8.1.0", "vscode-languageserver-types": "3.17.3" } }, "vscode-languageserver-types": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, "word-wrap": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } pygls-1.3.0/examples/vscode-playground/package.json000066400000000000000000000116551455570166200224450ustar00rootroot00000000000000{ "name": "pygls-playground", "description": "Extension for experimenting with pygls powered language servers", "author": "Open Law Library", "repository": "https://github.com/openlawlibrary/pygls", "license": "Apache-2.0", "version": "1.0.2", "publisher": "openlawlibrary", "engines": { "node": ">=16.17.1", "vscode": "^1.78.0" }, "extensionDependencies": [ "ms-python.python" ], "categories": [ "Programming Languages" ], "activationEvents": [ "onStartupFinished" ], "contributes": { "commands": [ { "command": "pygls.server.restart", "title": "Restart Language Server", "category": "pygls" }, { "command": "pygls.server.executeCommand", "title": "Execute Command", "category": "pygls" } ], "configuration": [ { "type": "object", "title": "Json Server Configuration", "properties": { "pygls.jsonServer.exampleConfiguration": { "scope": "resource", "type": "string", "default": "You can override this message" } } }, { "type": "object", "title": "Server Configuration", "properties": { "pygls.server.cwd": { "scope": "resource", "type": "string", "description": "The working directory from which to launch the server.", "markdownDescription": "The working directory from which to launch the server.\nIf blank, this will default to the `examples/servers` directory." }, "pygls.server.debug": { "scope": "resource", "default": false, "type": "boolean", "description": "Enable debugging of the server process." }, "pygls.server.debugHost": { "scope": "resource", "default": "localhost", "type": "string", "description": "The host on which the server process to debug is running." }, "pygls.server.debugPort": { "scope": "resource", "default": 5678, "type": "integer", "description": "The port number on which the server process to debug is listening." }, "pygls.server.launchScript": { "scope": "resource", "type": "string", "default": "json_server.py", "description": "The python script to run when launching the server.", "markdownDescription": "The python script to run when launching the server.\n Relative to #pygls.server.cwd#" }, "pygls.server.pythonPath": { "scope": "resource", "type": "string", "default": "", "description": "The python interpreter to use to run the server.\nBy default, this extension will attempt to use the Python interpreter configured via the Python extension, setting this setting will override this behavior." }, "pygls.trace.server": { "scope": "resource", "type": "string", "default": "off", "enum": [ "off", "messages", "verbose" ], "description": "Controls if LSP messages send to/from the server should be logged.", "enumDescriptions": [ "do not log any lsp messages", "log all lsp messages sent to/from the server", "log all lsp messages sent to/from the server, including their contents" ] } } }, { "type": "object", "title": "Client Configuration", "properties": { "pygls.client.documentSelector": { "scope": "window", "type": "array", "items": { "type": "object" }, "default": [ { "scheme": "file", "language": "json" } ], "description": "The client uses this to decide which documents the server is able to help with.", "markdownDescription": "The client uses this to decide which documents the server is able to help with.\n See [DocumentSelector](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentFilter) in the LSP Specification for more details." } } } ] }, "main": "./out/extension", "scripts": { "vscode:prepublish": "npm run compile", "compile": "tsc -p .", "watch": "tsc -p . -w" }, "devDependencies": { "@types/node": "^16.11.6", "@types/semver": "^7.5.0", "@types/vscode": "^1.78.0", "@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/parser": "^5.3.0", "eslint": "^8.2.0", "typescript": "^5.1.0" }, "dependencies": { "@vscode/python-extension": "^1.0.4", "semver": "^7.5.4", "vscode-languageclient": "^8.1.0" } } pygls-1.3.0/examples/vscode-playground/src/000077500000000000000000000000001455570166200207365ustar00rootroot00000000000000pygls-1.3.0/examples/vscode-playground/src/extension.ts000066400000000000000000000312061455570166200233240ustar00rootroot00000000000000/* ------------------------------------------------------------------------- * Original work Copyright (c) Microsoft Corporation. All rights reserved. * Original work licensed under the MIT License. * See ThirdPartyNotices.txt in the project root for license information. * All modifications Copyright (c) Open Law Library. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License") * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http: // www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ----------------------------------------------------------------------- */ "use strict"; import * as net from "net"; import * as path from "path"; import * as vscode from "vscode"; import * as semver from "semver"; import { PythonExtension } from "@vscode/python-extension"; import { LanguageClient, LanguageClientOptions, ServerOptions, State, integer } from "vscode-languageclient/node"; const MIN_PYTHON = semver.parse("3.7.9") // Some other nice to haves. // TODO: Check selected env satisfies pygls' requirements - if not offer to run the select env command. // TODO: TCP Transport // TODO: WS Transport // TODO: Web Extension support (requires WASM-WASI!) let client: LanguageClient; let clientStarting = false let python: PythonExtension; let logger: vscode.LogOutputChannel /** * This is the main entry point. * Called when vscode first activates the extension */ export async function activate(context: vscode.ExtensionContext) { logger = vscode.window.createOutputChannel('pygls', { log: true }) logger.info("Extension activated.") await getPythonExtension(); if (!python) { return } // Restart language server command context.subscriptions.push( vscode.commands.registerCommand("pygls.server.restart", async () => { logger.info('restarting server...') await startLangServer() }) ) // Execute command... command context.subscriptions.push( vscode.commands.registerCommand("pygls.server.executeCommand", async () => { await executeServerCommand() }) ) // Restart the language server if the user switches Python envs... context.subscriptions.push( python.environments.onDidChangeActiveEnvironmentPath(async () => { logger.info('python env modified, restarting server...') await startLangServer() }) ) // ... or if they change a relevant config option context.subscriptions.push( vscode.workspace.onDidChangeConfiguration(async (event) => { if (event.affectsConfiguration("pygls.server") || event.affectsConfiguration("pygls.client")) { logger.info('config modified, restarting server...') await startLangServer() } }) ) // Start the language server once the user opens the first text document... context.subscriptions.push( vscode.workspace.onDidOpenTextDocument( async () => { if (!client) { await startLangServer() } } ) ) // ...or notebook. context.subscriptions.push( vscode.workspace.onDidOpenNotebookDocument( async () => { if (!client) { await startLangServer() } } ) ) // Restart the server if the user modifies it. context.subscriptions.push( vscode.workspace.onDidSaveTextDocument(async (document: vscode.TextDocument) => { const expectedUri = vscode.Uri.file(path.join(getCwd(), getServerPath())) if (expectedUri.toString() === document.uri.toString()) { logger.info('server modified, restarting...') await startLangServer() } }) ) } export function deactivate(): Thenable { return stopLangServer() } /** * Start (or restart) the language server. * * @param command The executable to run * @param args Arguments to pass to the executable * @param cwd The working directory in which to run the executable * @returns */ async function startLangServer() { // Don't interfere if we are already in the process of launching the server. if (clientStarting) { return } clientStarting = true if (client) { await stopLangServer() } const config = vscode.workspace.getConfiguration("pygls.server") const cwd = getCwd() const serverPath = getServerPath() logger.info(`cwd: '${cwd}'`) logger.info(`server: '${serverPath}'`) const resource = vscode.Uri.joinPath(vscode.Uri.file(cwd), serverPath) const pythonCommand = await getPythonCommand(resource) if (!pythonCommand) { clientStarting = false return } logger.debug(`python: ${pythonCommand.join(" ")}`) const serverOptions: ServerOptions = { command: pythonCommand[0], args: [...pythonCommand.slice(1), serverPath], options: { cwd }, }; client = new LanguageClient('pygls', serverOptions, getClientOptions()); const promises = [client.start()] if (config.get("debug")) { promises.push(startDebugging()) } const results = await Promise.allSettled(promises) clientStarting = false for (const result of results) { if (result.status === "rejected") { logger.error(`There was a error starting the server: ${result.reason}`) } } } async function stopLangServer(): Promise { if (!client) { return } if (client.state === State.Running) { await client.stop() } client.dispose() client = undefined } function startDebugging(): Promise { if (!vscode.workspace.workspaceFolders) { logger.error("Unable to start debugging, there is no workspace.") return Promise.reject("Unable to start debugging, there is no workspace.") } // TODO: Is there a more reliable way to ensure the debug adapter is ready? setTimeout(async () => { await vscode.debug.startDebugging(vscode.workspace.workspaceFolders[0], "pygls: Debug Server") }, 2000) } function getClientOptions(): LanguageClientOptions { const config = vscode.workspace.getConfiguration('pygls.client') const options = { documentSelector: config.get('documentSelector'), outputChannel: logger, connectionOptions: { maxRestartCount: 0 // don't restart on server failure. }, }; logger.info(`client options: ${JSON.stringify(options, undefined, 2)}`) return options } function startLangServerTCP(addr: number): LanguageClient { const serverOptions: ServerOptions = () => { return new Promise((resolve /*, reject */) => { const clientSocket = new net.Socket(); clientSocket.connect(addr, "127.0.0.1", () => { resolve({ reader: clientSocket, writer: clientSocket, }); }); }); }; return new LanguageClient( `tcp lang server (port ${addr})`, serverOptions, getClientOptions() ); } /** * Execute a command provided by the language server. */ async function executeServerCommand() { if (!client || client.state !== State.Running) { await vscode.window.showErrorMessage("There is no language server running.") return } const knownCommands = client.initializeResult.capabilities.executeCommandProvider?.commands if (!knownCommands || knownCommands.length === 0) { const info = client.initializeResult.serverInfo const name = info?.name || "Server" const version = info?.version || "" await vscode.window.showInformationMessage(`${name} ${version} does not implement any commands.`) return } const commandName = await vscode.window.showQuickPick(knownCommands, { canPickMany: false }) if (!commandName) { return } logger.info(`executing command: '${commandName}'`) const result = await vscode.commands.executeCommand(commandName /* if your command accepts arguments you can pass them here */) logger.info(`${commandName} result: ${JSON.stringify(result, undefined, 2)}`) } /** * If the user has explicitly provided a src directory use that. * Otherwise, fallback to the examples/servers directory. * * @returns The working directory from which to launch the server */ function getCwd(): string { const config = vscode.workspace.getConfiguration("pygls.server") const cwd = config.get('cwd') if (cwd) { return cwd } const serverDir = path.resolve( path.join(__dirname, "..", "..", "servers") ) return serverDir } /** * * @returns The python script that implements the server. */ function getServerPath(): string { const config = vscode.workspace.getConfiguration("pygls.server") const server = config.get('launchScript') return server } /** * Return the python command to use when starting the server. * * If debugging is enabled, this will also included the arguments to required * to wrap the server in a debug adapter. * * @returns The full python command needed in order to start the server. */ async function getPythonCommand(resource?: vscode.Uri): Promise { const config = vscode.workspace.getConfiguration("pygls.server", resource) const pythonPath = await getPythonInterpreter(resource) if (!pythonPath) { return } const command = [pythonPath] const enableDebugger = config.get('debug') if (!enableDebugger) { return command } const debugHost = config.get('debugHost') const debugPort = config.get('debugPort') try { const debugArgs = await python.debug.getRemoteLauncherCommand(debugHost, debugPort, true) // Debugpy recommends we disable frozen modules command.push("-Xfrozen_modules=off", ...debugArgs) } catch (err) { logger.error(`Unable to get debugger command: ${err}`) logger.error("Debugger will not be available.") } return command } /** * Return the python interpreter to use when starting the server. * * This uses the official python extension to grab the user's currently * configured environment. * * @returns The python interpreter to use to launch the server */ async function getPythonInterpreter(resource?: vscode.Uri): Promise { const config = vscode.workspace.getConfiguration("pygls.server", resource) const pythonPath = config.get('pythonPath') if (pythonPath) { logger.info(`Using user configured python environment: '${pythonPath}'`) return pythonPath } if (!python) { return } if (resource) { logger.info(`Looking for environment in which to execute: '${resource.toString()}'`) } // Use whichever python interpreter the user has configured. const activeEnvPath = python.environments.getActiveEnvironmentPath(resource) logger.info(`Found environment: ${activeEnvPath.id}: ${activeEnvPath.path}`) const activeEnv = await python.environments.resolveEnvironment(activeEnvPath) if (!activeEnv) { logger.error(`Unable to resolve envrionment: ${activeEnvPath}`) return } const v = activeEnv.version const pythonVersion = semver.parse(`${v.major}.${v.minor}.${v.micro}`) // Check to see if the environment satisfies the min Python version. if (semver.lt(pythonVersion, MIN_PYTHON)) { const message = [ `Your currently configured environment provides Python v${pythonVersion} `, `but pygls requires v${MIN_PYTHON}.\n\nPlease choose another environment.` ].join('') const response = await vscode.window.showErrorMessage(message, "Change Environment") if (!response) { return } else { await vscode.commands.executeCommand('python.setInterpreter') return } } const pythonUri = activeEnv.executable.uri if (!pythonUri) { logger.error(`URI of Python executable is undefined!`) return } return pythonUri.fsPath } async function getPythonExtension() { try { python = await PythonExtension.api(); } catch (err) { logger.error(`Unable to load python extension: ${err}`) } } pygls-1.3.0/examples/vscode-playground/tsconfig.json000066400000000000000000000004601455570166200226560ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es2019", "lib": [ "ES2019" ], "rootDir": "src", "outDir": "out", "sourceMap": true }, "include": [ "src" ], "exclude": [ "node_modules" ] } pygls-1.3.0/poetry.lock000066400000000000000000002662711455570166200150760ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" optional = false python-versions = ">=3.6" files = [ {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, ] [[package]] name = "attrs" version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "babel" version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "black" version = "23.12.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, ] [package.dependencies] click = ">=8.0.0" mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cattrs" version = "23.2.3" description = "Composable complex class support for attrs and dataclasses." optional = false python-versions = ">=3.8" files = [ {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, ] [package.dependencies] attrs = ">=23.1.0" exceptiongroup = {version = ">=1.1.1", markers = "python_version < \"3.11\""} typing-extensions = {version = ">=4.1.0,<4.6.3 || >4.6.3", markers = "python_version < \"3.11\""} [package.extras] bson = ["pymongo (>=4.4.0)"] cbor2 = ["cbor2 (>=5.4.6)"] msgpack = ["msgpack (>=1.0.5)"] orjson = ["orjson (>=3.9.2)"] pyyaml = ["pyyaml (>=6.0)"] tomlkit = ["tomlkit (>=0.11.8)"] ujson = ["ujson (>=5.7.0)"] [[package]] name = "certifi" version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] [[package]] name = "cffi" version = "1.16.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, ] [package.dependencies] pycparser = "*" [[package]] name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "click" version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.4.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" files = [ {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] [[package]] name = "idna" version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] [[package]] name = "importlib-metadata" version = "7.0.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "jinja2" version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "lsprotocol" version = "2023.0.1" description = "Python implementation of the Language Server Protocol." optional = false python-versions = ">=3.7" files = [ {file = "lsprotocol-2023.0.1-py3-none-any.whl", hash = "sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2"}, {file = "lsprotocol-2023.0.1.tar.gz", hash = "sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d"}, ] [package.dependencies] attrs = ">=21.3.0" cattrs = "!=23.2.1" [[package]] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] [[package]] name = "mypy" version = "1.8.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "outcome" version = "1.3.0.post0" description = "Capture the outcome of Python function calls." optional = false python-versions = ">=3.7" files = [ {file = "outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b"}, {file = "outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8"}, ] [package.dependencies] attrs = ">=19.2.0" [[package]] name = "packaging" version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pastel" version = "0.2.1" description = "Bring colors to your terminal." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, ] [[package]] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "platformdirs" version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" files = [ {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, ] [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "poethepoet" version = "0.24.4" description = "A task runner that works well with poetry." optional = false python-versions = ">=3.8" files = [ {file = "poethepoet-0.24.4-py3-none-any.whl", hash = "sha256:fb4ea35d7f40fe2081ea917d2e4102e2310fda2cde78974050ca83896e229075"}, {file = "poethepoet-0.24.4.tar.gz", hash = "sha256:ff4220843a87c888cbcb5312c8905214701d0af60ac7271795baa8369b428fef"}, ] [package.dependencies] pastel = ">=0.2.1,<0.3.0" tomli = ">=1.2.2" [package.extras] poetry-plugin = ["poetry (>=1.0,<2.0)"] [[package]] name = "pycparser" version = "2.21" description = "C parser in Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] [[package]] name = "pygments" version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pysocks" version = "1.7.1" description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, ] [[package]] name = "pytest" version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" version = "0.23.3" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ {file = "pytest-asyncio-0.23.3.tar.gz", hash = "sha256:af313ce900a62fbe2b1aed18e37ad757f1ef9940c6b6a88e2954de38d6b1fb9f"}, {file = "pytest_asyncio-0.23.3-py3-none-any.whl", hash = "sha256:37a9d912e8338ee7b4a3e917381d1c95bfc8682048cb0fbc35baba316ec1faba"}, ] [package.dependencies] pytest = ">=7.0.0" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] [[package]] name = "pytz" version = "2023.3.post1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" version = "0.1.13" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ {file = "ruff-0.1.13-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e3fd36e0d48aeac672aa850045e784673449ce619afc12823ea7868fcc41d8ba"}, {file = "ruff-0.1.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9fb6b3b86450d4ec6a6732f9f60c4406061b6851c4b29f944f8c9d91c3611c7a"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b13ba5d7156daaf3fd08b6b993360a96060500aca7e307d95ecbc5bb47a69296"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ebb40442f7b531e136d334ef0851412410061e65d61ca8ce90d894a094feb22"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226b517f42d59a543d6383cfe03cccf0091e3e0ed1b856c6824be03d2a75d3b6"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5f0312ba1061e9b8c724e9a702d3c8621e3c6e6c2c9bd862550ab2951ac75c16"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2f59bcf5217c661254bd6bc42d65a6fd1a8b80c48763cb5c2293295babd945dd"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6894b00495e00c27b6ba61af1fc666f17de6140345e5ef27dd6e08fb987259d"}, {file = "ruff-0.1.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1600942485c6e66119da294c6294856b5c86fd6df591ce293e4a4cc8e72989"}, {file = "ruff-0.1.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ee3febce7863e231a467f90e681d3d89210b900d49ce88723ce052c8761be8c7"}, {file = "ruff-0.1.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dcaab50e278ff497ee4d1fe69b29ca0a9a47cd954bb17963628fa417933c6eb1"}, {file = "ruff-0.1.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f57de973de4edef3ad3044d6a50c02ad9fc2dff0d88587f25f1a48e3f72edf5e"}, {file = "ruff-0.1.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a36fa90eb12208272a858475ec43ac811ac37e91ef868759770b71bdabe27b6"}, {file = "ruff-0.1.13-py3-none-win32.whl", hash = "sha256:a623349a505ff768dad6bd57087e2461be8db58305ebd5577bd0e98631f9ae69"}, {file = "ruff-0.1.13-py3-none-win_amd64.whl", hash = "sha256:f988746e3c3982bea7f824c8fa318ce7f538c4dfefec99cd09c8770bd33e6539"}, {file = "ruff-0.1.13-py3-none-win_arm64.whl", hash = "sha256:6bbbc3042075871ec17f28864808540a26f0f79a4478c357d3e3d2284e832998"}, {file = "ruff-0.1.13.tar.gz", hash = "sha256:e261f1baed6291f434ffb1d5c6bd8051d1c2a26958072d38dfbec39b3dda7352"}, ] [[package]] name = "selenium" version = "4.16.0" description = "" optional = false python-versions = ">=3.8" files = [ {file = "selenium-4.16.0-py3-none-any.whl", hash = "sha256:aec71f4e6ed6cb3ec25c9c1b5ed56ae31b6da0a7f17474c7566d303f84e6219f"}, {file = "selenium-4.16.0.tar.gz", hash = "sha256:b2e987a445306151f7be0e6dfe2aa72a479c2ac6a91b9d5ef2d6dd4e49ad0435"}, ] [package.dependencies] certifi = ">=2021.10.8" trio = ">=0.17,<1.0" trio-websocket = ">=0.9,<1.0" urllib3 = {version = ">=1.26,<3", extras = ["socks"]} [[package]] name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" files = [ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] [[package]] name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] [[package]] name = "sortedcontainers" version = "2.4.0" description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" optional = false python-versions = "*" files = [ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] [[package]] name = "sphinx" version = "7.1.2" description = "Python documentation generator" optional = false python-versions = ">=3.8" files = [ {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"}, {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"}, ] [package.dependencies] alabaster = ">=0.7,<0.8" babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.13" requests = ">=2.25.0" snowballstemmer = ">=2.0" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] [[package]] name = "sphinx-rtd-theme" version = "2.0.0" description = "Read the Docs theme for Sphinx" optional = false python-versions = ">=3.6" files = [ {file = "sphinx_rtd_theme-2.0.0-py2.py3-none-any.whl", hash = "sha256:ec93d0856dc280cf3aee9a4c9807c60e027c7f7b461b77aeffed682e68f0e586"}, {file = "sphinx_rtd_theme-2.0.0.tar.gz", hash = "sha256:bd5d7b80622406762073a04ef8fadc5f9151261563d47027de09910ce03afe6b"}, ] [package.dependencies] docutils = "<0.21" sphinx = ">=5,<8" sphinxcontrib-jquery = ">=4,<5" [package.extras] dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] [[package]] name = "sphinxcontrib-applehelp" version = "1.0.4" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.8" files = [ {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.8" files = [ {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jquery" version = "4.1" description = "Extension to include jQuery on newer Sphinx releases" optional = false python-versions = ">=2.7" files = [ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"}, {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"}, ] [package.dependencies] Sphinx = ">=1.8" [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] [package.extras] test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "trio" version = "0.24.0" description = "A friendly Python library for async concurrency and I/O" optional = false python-versions = ">=3.8" files = [ {file = "trio-0.24.0-py3-none-any.whl", hash = "sha256:c3bd3a4e3e3025cd9a2241eae75637c43fe0b9e88b4c97b9161a55b9e54cd72c"}, {file = "trio-0.24.0.tar.gz", hash = "sha256:ffa09a74a6bf81b84f8613909fb0beaee84757450183a7a2e0b47b455c0cac5d"}, ] [package.dependencies] attrs = ">=20.1.0" cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""} exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} idna = "*" outcome = "*" sniffio = ">=1.3.0" sortedcontainers = "*" [[package]] name = "trio-websocket" version = "0.11.1" description = "WebSocket library for Trio" optional = false python-versions = ">=3.7" files = [ {file = "trio-websocket-0.11.1.tar.gz", hash = "sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f"}, {file = "trio_websocket-0.11.1-py3-none-any.whl", hash = "sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638"}, ] [package.dependencies] exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} trio = ">=0.11" wsproto = ">=0.14" [[package]] name = "typing-extensions" version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] name = "urllib3" version = "2.1.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, ] [package.dependencies] pysocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""} [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "websockets" version = "12.0" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = true python-versions = ">=3.8" files = [ {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, ] [[package]] name = "wsproto" version = "1.2.0" description = "WebSockets state-machine based protocol implementation" optional = false python-versions = ">=3.7.0" files = [ {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, ] [package.dependencies] h11 = ">=0.9.0,<1" [[package]] name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [extras] ws = ["websockets"] [metadata] lock-version = "2.0" python-versions = ">=3.8" content-hash = "2ec8c8dbebbb73092a1cfb6c37a4550714a46830aa0e3267ea3c1010a72a49e7" pygls-1.3.0/pygls/000077500000000000000000000000001455570166200140225ustar00rootroot00000000000000pygls-1.3.0/pygls/__init__.py000066400000000000000000000027201455570166200161340ustar00rootroot00000000000000############################################################################ # Original work Copyright 2018 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import os import sys IS_WIN = os.name == "nt" IS_PYODIDE = "pyodide" in sys.modules pygls = "pygls" pygls-1.3.0/pygls/capabilities.py000066400000000000000000000405641455570166200170360ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from functools import reduce from typing import Any, Dict, List, Optional, Set, Union, TypeVar import logging from lsprotocol import types logger = logging.getLogger(__name__) T = TypeVar("T") def get_capability( client_capabilities: types.ClientCapabilities, field: str, default: Any = None ) -> Any: """Check if ClientCapabilities has some nested value without raising AttributeError. e.g. get_capability('text_document.synchronization.will_save') """ try: value = reduce(getattr, field.split("."), client_capabilities) except AttributeError: return default # If we reach the desired leaf value but it's None, return the default. return default if value is None else value class ServerCapabilitiesBuilder: """Create `ServerCapabilities` instance depending on builtin and user registered features. """ def __init__( self, client_capabilities: types.ClientCapabilities, features: Set[str], feature_options: Dict[str, Any], commands: List[str], text_document_sync_kind: types.TextDocumentSyncKind, notebook_document_sync: Optional[types.NotebookDocumentSyncOptions] = None, ): self.client_capabilities = client_capabilities self.features = features self.feature_options = feature_options self.commands = commands self.text_document_sync_kind = text_document_sync_kind self.notebook_document_sync = notebook_document_sync self.server_cap = types.ServerCapabilities() def _provider_options(self, feature: str, default: T) -> Optional[Union[T, Any]]: if feature in self.features: return self.feature_options.get(feature, default) return None def _with_text_document_sync(self): open_close = ( types.TEXT_DOCUMENT_DID_OPEN in self.features or types.TEXT_DOCUMENT_DID_CLOSE in self.features ) will_save = ( get_capability( self.client_capabilities, "text_document.synchronization.will_save" ) and types.TEXT_DOCUMENT_WILL_SAVE in self.features ) will_save_wait_until = ( get_capability( self.client_capabilities, "text_document.synchronization.will_save_wait_until", ) and types.TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL in self.features ) if types.TEXT_DOCUMENT_DID_SAVE in self.features: save = self.feature_options.get(types.TEXT_DOCUMENT_DID_SAVE, True) else: save = False self.server_cap.text_document_sync = types.TextDocumentSyncOptions( open_close=open_close, change=self.text_document_sync_kind, will_save=will_save, will_save_wait_until=will_save_wait_until, save=save, ) return self def _with_notebook_document_sync(self): if self.client_capabilities.notebook_document is None: return self self.server_cap.notebook_document_sync = self.notebook_document_sync return self def _with_completion(self): value = self._provider_options( types.TEXT_DOCUMENT_COMPLETION, default=types.CompletionOptions() ) if value is not None: self.server_cap.completion_provider = value return self def _with_hover(self): value = self._provider_options(types.TEXT_DOCUMENT_HOVER, default=True) if value is not None: self.server_cap.hover_provider = value return self def _with_signature_help(self): value = self._provider_options( types.TEXT_DOCUMENT_SIGNATURE_HELP, default=types.SignatureHelpOptions() ) if value is not None: self.server_cap.signature_help_provider = value return self def _with_declaration(self): value = self._provider_options(types.TEXT_DOCUMENT_DECLARATION, default=True) if value is not None: self.server_cap.declaration_provider = value return self def _with_definition(self): value = self._provider_options(types.TEXT_DOCUMENT_DEFINITION, default=True) if value is not None: self.server_cap.definition_provider = value return self def _with_type_definition(self): value = self._provider_options( types.TEXT_DOCUMENT_TYPE_DEFINITION, default=types.TypeDefinitionOptions() ) if value is not None: self.server_cap.type_definition_provider = value return self def _with_inlay_hints(self): value = self._provider_options( types.TEXT_DOCUMENT_INLAY_HINT, default=types.InlayHintOptions() ) if value is not None: value.resolve_provider = types.INLAY_HINT_RESOLVE in self.features self.server_cap.inlay_hint_provider = value return self def _with_implementation(self): value = self._provider_options( types.TEXT_DOCUMENT_IMPLEMENTATION, default=types.ImplementationOptions() ) if value is not None: self.server_cap.implementation_provider = value return self def _with_references(self): value = self._provider_options(types.TEXT_DOCUMENT_REFERENCES, default=True) if value is not None: self.server_cap.references_provider = value return self def _with_document_highlight(self): value = self._provider_options( types.TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, default=True ) if value is not None: self.server_cap.document_highlight_provider = value return self def _with_document_symbol(self): value = self._provider_options( types.TEXT_DOCUMENT_DOCUMENT_SYMBOL, default=True ) if value is not None: self.server_cap.document_symbol_provider = value return self def _with_code_action(self): value = self._provider_options(types.TEXT_DOCUMENT_CODE_ACTION, default=True) if value is not None: self.server_cap.code_action_provider = value return self def _with_code_lens(self): value = self._provider_options( types.TEXT_DOCUMENT_CODE_LENS, default=types.CodeLensOptions() ) if value is not None: self.server_cap.code_lens_provider = value return self def _with_document_link(self): value = self._provider_options( types.TEXT_DOCUMENT_DOCUMENT_LINK, default=types.DocumentLinkOptions() ) if value is not None: self.server_cap.document_link_provider = value return self def _with_color(self): value = self._provider_options(types.TEXT_DOCUMENT_DOCUMENT_COLOR, default=True) if value is not None: self.server_cap.color_provider = value return self def _with_document_formatting(self): value = self._provider_options(types.TEXT_DOCUMENT_FORMATTING, default=True) if value is not None: self.server_cap.document_formatting_provider = value return self def _with_document_range_formatting(self): value = self._provider_options( types.TEXT_DOCUMENT_RANGE_FORMATTING, default=True ) if value is not None: self.server_cap.document_range_formatting_provider = value return self def _with_document_on_type_formatting(self): value = self._provider_options( types.TEXT_DOCUMENT_ON_TYPE_FORMATTING, default=None ) if value is not None: self.server_cap.document_on_type_formatting_provider = value return self def _with_rename(self): value = self._provider_options(types.TEXT_DOCUMENT_RENAME, default=True) if value is not None: self.server_cap.rename_provider = value return self def _with_folding_range(self): value = self._provider_options(types.TEXT_DOCUMENT_FOLDING_RANGE, default=True) if value is not None: self.server_cap.folding_range_provider = value return self def _with_execute_command(self): self.server_cap.execute_command_provider = types.ExecuteCommandOptions( commands=self.commands ) return self def _with_selection_range(self): value = self._provider_options( types.TEXT_DOCUMENT_SELECTION_RANGE, default=True ) if value is not None: self.server_cap.selection_range_provider = value return self def _with_call_hierarchy(self): value = self._provider_options( types.TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, default=True ) if value is not None: self.server_cap.call_hierarchy_provider = value return self def _with_type_hierarchy(self): value = self._provider_options( types.TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY, default=True ) if value is not None: self.server_cap.type_hierarchy_provider = value return self def _with_semantic_tokens(self): providers = [ types.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, types.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, types.TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, ] value = None for provider in providers: value = self._provider_options(provider, default=None) if value is not None: break if value is None: return self if isinstance(value, types.SemanticTokensRegistrationOptions): self.server_cap.semantic_tokens_provider = value return self full_support: Union[bool, types.SemanticTokensOptionsFullType1] = ( types.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL in self.features ) if types.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA in self.features: full_support = types.SemanticTokensOptionsFullType1(delta=True) options = types.SemanticTokensOptions( legend=value, full=full_support or None, range=types.TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE in self.features or None, ) if options.full or options.range: self.server_cap.semantic_tokens_provider = options return self def _with_linked_editing_range(self): value = self._provider_options( types.TEXT_DOCUMENT_LINKED_EDITING_RANGE, default=True ) if value is not None: self.server_cap.linked_editing_range_provider = value return self def _with_moniker(self): value = self._provider_options(types.TEXT_DOCUMENT_MONIKER, default=True) if value is not None: self.server_cap.moniker_provider = value return self def _with_workspace_symbol(self): value = self._provider_options( types.WORKSPACE_SYMBOL, default=types.WorkspaceSymbolOptions() ) if value is not None: value.resolve_provider = types.WORKSPACE_SYMBOL_RESOLVE in self.features self.server_cap.workspace_symbol_provider = value return self def _with_workspace_capabilities(self): # File operations file_operations = types.FileOperationOptions() operations = [ (types.WORKSPACE_WILL_CREATE_FILES, "will_create"), (types.WORKSPACE_DID_CREATE_FILES, "did_create"), (types.WORKSPACE_WILL_DELETE_FILES, "will_delete"), (types.WORKSPACE_DID_DELETE_FILES, "did_delete"), (types.WORKSPACE_WILL_RENAME_FILES, "will_rename"), (types.WORKSPACE_DID_RENAME_FILES, "did_rename"), ] for method_name, capability_name in operations: client_supports_method = get_capability( self.client_capabilities, f"workspace.file_operations.{capability_name}" ) if client_supports_method: value = self._provider_options(method_name, default=None) setattr(file_operations, capability_name, value) self.server_cap.workspace = types.ServerCapabilitiesWorkspaceType( workspace_folders=types.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True, ), file_operations=file_operations, ) return self def _with_diagnostic_provider(self): value = self._provider_options( types.TEXT_DOCUMENT_DIAGNOSTIC, default=types.DiagnosticOptions( inter_file_dependencies=False, workspace_diagnostics=False ), ) if value is not None: value.workspace_diagnostics = types.WORKSPACE_DIAGNOSTIC in self.features self.server_cap.diagnostic_provider = value return self def _with_inline_value_provider(self): value = self._provider_options(types.TEXT_DOCUMENT_INLINE_VALUE, default=True) if value is not None: self.server_cap.inline_value_provider = value return self def _with_position_encodings(self): self.server_cap.position_encoding = types.PositionEncodingKind.Utf16 general = self.client_capabilities.general if general is None: return self encodings = general.position_encodings if encodings is None: return self if types.PositionEncodingKind.Utf16 in encodings: return self if types.PositionEncodingKind.Utf32 in encodings: self.server_cap.position_encoding = types.PositionEncodingKind.Utf32 return self if types.PositionEncodingKind.Utf8 in encodings: self.server_cap.position_encoding = types.PositionEncodingKind.Utf8 return self logger.warning(f"Unknown `PositionEncoding`s: {encodings}") return self def _build(self): return self.server_cap def build(self): return ( self._with_text_document_sync() ._with_notebook_document_sync() ._with_completion() ._with_hover() ._with_signature_help() ._with_declaration() ._with_definition() ._with_type_definition() ._with_inlay_hints() ._with_implementation() ._with_references() ._with_document_highlight() ._with_document_symbol() ._with_code_action() ._with_code_lens() ._with_document_link() ._with_color() ._with_document_formatting() ._with_document_range_formatting() ._with_document_on_type_formatting() ._with_rename() ._with_folding_range() ._with_execute_command() ._with_selection_range() ._with_call_hierarchy() ._with_type_hierarchy() ._with_semantic_tokens() ._with_linked_editing_range() ._with_moniker() ._with_workspace_symbol() ._with_workspace_capabilities() ._with_diagnostic_provider() ._with_inline_value_provider() ._with_position_encodings() ._build() ) pygls-1.3.0/pygls/client.py000066400000000000000000000142241455570166200156550ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio import logging import re from threading import Event from typing import Any from typing import Callable from typing import List from typing import Optional from typing import Type from typing import Union from cattrs import Converter from pygls.exceptions import PyglsError, JsonRpcException from pygls.protocol import JsonRPCProtocol, default_converter logger = logging.getLogger(__name__) async def aio_readline(stop_event, reader, message_handler): CONTENT_LENGTH_PATTERN = re.compile(rb"^Content-Length: (\d+)\r\n$") # Initialize message buffer message = [] content_length = 0 while not stop_event.is_set(): # Read a header line header = await reader.readline() if not header: break message.append(header) # Extract content length if possible if not content_length: match = CONTENT_LENGTH_PATTERN.fullmatch(header) if match: content_length = int(match.group(1)) logger.debug("Content length: %s", content_length) # Check if all headers have been read (as indicated by an empty line \r\n) if content_length and not header.strip(): # Read body body = await reader.readexactly(content_length) if not body: break message.append(body) # Pass message to protocol message_handler(b"".join(message)) # Reset the buffer message = [] content_length = 0 class JsonRPCClient: """Base JSON-RPC client.""" def __init__( self, protocol_cls: Type[JsonRPCProtocol] = JsonRPCProtocol, converter_factory: Callable[[], Converter] = default_converter, ): # Strictly speaking `JsonRPCProtocol` wants a `LanguageServer`, not a # `JsonRPCClient`. However there similar enough for our purposes, which is # that this client will mostly be used in testing contexts. self.protocol = protocol_cls(self, converter_factory()) # type: ignore self._server: Optional[asyncio.subprocess.Process] = None self._stop_event = Event() self._async_tasks: List[asyncio.Task] = [] @property def stopped(self) -> bool: """Return ``True`` if the client has been stopped.""" return self._stop_event.is_set() def feature( self, feature_name: str, options: Optional[Any] = None, ): """Decorator used to register LSP features. Example ------- :: import logging from pygls.client import JsonRPCClient ls = JsonRPCClient() @ls.feature('window/logMessage') def completions(ls, params): logging.info("%s", params.message) """ return self.protocol.fm.feature(feature_name, options) async def start_io(self, cmd: str, *args, **kwargs): """Start the given server and communicate with it over stdio.""" logger.debug("Starting server process: %s", " ".join([cmd, *args])) server = await asyncio.create_subprocess_exec( cmd, *args, stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, **kwargs, ) self.protocol.connection_made(server.stdin) # type: ignore connection = asyncio.create_task( aio_readline(self._stop_event, server.stdout, self.protocol.data_received) ) notify_exit = asyncio.create_task(self._server_exit()) self._server = server self._async_tasks.extend([connection, notify_exit]) async def _server_exit(self): if self._server is not None: await self._server.wait() logger.debug( "Server process %s exited with return code: %s", self._server.pid, self._server.returncode, ) await self.server_exit(self._server) self._stop_event.set() async def server_exit(self, server: asyncio.subprocess.Process): """Called when the server process exits.""" def _report_server_error( self, error: Exception, source: Union[PyglsError, JsonRpcException] ): try: self.report_server_error(error, source) except Exception: logger.error("Unable to report error", exc_info=True) def report_server_error( self, error: Exception, source: Union[PyglsError, JsonRpcException] ): """Called when the server does something unexpected e.g. respond with malformed JSON.""" async def stop(self): self._stop_event.set() if self._server is not None and self._server.returncode is None: logger.debug("Terminating server process: %s", self._server.pid) self._server.terminate() if len(self._async_tasks) > 0: await asyncio.gather(*self._async_tasks) pygls-1.3.0/pygls/constants.py000066400000000000000000000026761455570166200164230ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ # Dynamically assigned attributes ATTR_EXECUTE_IN_THREAD = "execute_in_thread" ATTR_COMMAND_TYPE = "command" ATTR_FEATURE_TYPE = "feature" ATTR_REGISTERED_NAME = "reg_name" ATTR_REGISTERED_TYPE = "reg_type" # Parameters PARAM_LS = "ls" pygls-1.3.0/pygls/exceptions.py000066400000000000000000000142361455570166200165630ustar00rootroot00000000000000############################################################################ # Original work Copyright 2018 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import traceback from typing import Set from typing import Type from lsprotocol.types import ResponseError class JsonRpcException(Exception): """A class used as a base class for json rpc exceptions.""" def __init__(self, message=None, code=None, data=None): message = message or getattr(self.__class__, "MESSAGE") super().__init__(message) self.message = message self.code = code or getattr(self.__class__, "CODE") self.data = data def __eq__(self, other): return ( isinstance(other, self.__class__) and self.code == other.code and self.message == other.message ) def __hash__(self): return hash((self.code, self.message)) @staticmethod def from_error(error): for exc_class in _EXCEPTIONS: if exc_class.supports_code(error.code): return exc_class( code=error.code, message=error.message, data=error.data ) return JsonRpcException(code=error.code, message=error.message, data=error.data) @classmethod def supports_code(cls, code): # Defaults to UnknownErrorCode return getattr(cls, "CODE", -32001) == code def to_response_error(self) -> ResponseError: return ResponseError(code=self.code, message=self.message, data=self.data) class JsonRpcInternalError(JsonRpcException): CODE = -32603 MESSAGE = "Internal Error" @classmethod def of(cls, exc_info): exc_type, exc_value, exc_tb = exc_info return cls( message="".join( traceback.format_exception_only(exc_type, exc_value) ).strip(), data={"traceback": traceback.format_tb(exc_tb)}, ) class JsonRpcInvalidParams(JsonRpcException): CODE = -32602 MESSAGE = "Invalid Params" class JsonRpcInvalidRequest(JsonRpcException): CODE = -32600 MESSAGE = "Invalid Request" class JsonRpcMethodNotFound(JsonRpcException): CODE = -32601 MESSAGE = "Method Not Found" @classmethod def of(cls, method): return cls(message=cls.MESSAGE + ": " + method) class JsonRpcParseError(JsonRpcException): CODE = -32700 MESSAGE = "Parse Error" class JsonRpcRequestCancelled(JsonRpcException): CODE = -32800 MESSAGE = "Request Cancelled" class JsonRpcContentModified(JsonRpcException): CODE = -32801 MESSAGE = "Content Modified" class JsonRpcServerNotInitialized(JsonRpcException): CODE = -32002 MESSAGE = "ServerNotInitialized" class JsonRpcUnknownErrorCode(JsonRpcException): CODE = -32001 MESSAGE = "UnknownErrorCode" class JsonRpcReservedErrorRangeStart(JsonRpcException): CODE = -32099 MESSAGE = "jsonrpcReservedErrorRangeStart" class JsonRpcReservedErrorRangeEnd(JsonRpcException): CODE = -32000 MESSAGE = "jsonrpcReservedErrorRangeEnd" class LspReservedErrorRangeStart(JsonRpcException): CODE = -32899 MESSAGE = "lspReservedErrorRangeStart" class LspReservedErrorRangeEnd(JsonRpcException): CODE = -32800 MESSAGE = "lspReservedErrorRangeEnd" class JsonRpcServerError(JsonRpcException): def __init__(self, message, code, data=None): if not _is_server_error_code(code): raise ValueError("Error code should be in range -32099 - -32000") super().__init__(message=message, code=code, data=data) @classmethod def supports_code(cls, code): return _is_server_error_code(code) def _is_server_error_code(code): return -32099 <= code <= -32000 _EXCEPTIONS: Set[Type[JsonRpcException]] = { JsonRpcInternalError, JsonRpcInvalidParams, JsonRpcInvalidRequest, JsonRpcMethodNotFound, JsonRpcParseError, JsonRpcRequestCancelled, JsonRpcServerError, } class PyglsError(Exception): pass class CommandAlreadyRegisteredError(PyglsError): def __init__(self, command_name): self.command_name = command_name def __repr__(self): return f'Command "{self.command_name}" is already registered.' class FeatureAlreadyRegisteredError(PyglsError): def __init__(self, feature_name): self.feature_name = feature_name def __repr__(self): return f'Feature "{self.feature_name}" is already registered.' class FeatureRequestError(PyglsError): pass class FeatureNotificationError(PyglsError): pass class MethodTypeNotRegisteredError(PyglsError): def __init__(self, name): self.name = name def __repr__(self): return f'"{self.name}" is not added to `pygls.lsp.LSP_METHODS_MAP`.' class ThreadDecoratorError(PyglsError): pass class ValidationError(PyglsError): def __init__(self, errors=None): self.errors = errors or [] def __repr__(self): opt_errs = "\n-".join([e for e in self.errors]) return f"Missing options: {opt_errs}" pygls-1.3.0/pygls/feature_manager.py000066400000000000000000000204561455570166200175300ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio import functools import inspect import itertools import logging from typing import Any, Callable, Dict, Optional, get_type_hints from pygls.constants import ( ATTR_COMMAND_TYPE, ATTR_EXECUTE_IN_THREAD, ATTR_FEATURE_TYPE, ATTR_REGISTERED_NAME, ATTR_REGISTERED_TYPE, PARAM_LS, ) from pygls.exceptions import ( CommandAlreadyRegisteredError, FeatureAlreadyRegisteredError, ThreadDecoratorError, ValidationError, ) from pygls.lsp import get_method_options_type, is_instance logger = logging.getLogger(__name__) def assign_help_attrs(f, reg_name, reg_type): setattr(f, ATTR_REGISTERED_NAME, reg_name) setattr(f, ATTR_REGISTERED_TYPE, reg_type) def assign_thread_attr(f): setattr(f, ATTR_EXECUTE_IN_THREAD, True) def get_help_attrs(f): return getattr(f, ATTR_REGISTERED_NAME, None), getattr( f, ATTR_REGISTERED_TYPE, None ) def has_ls_param_or_annotation(f, annotation): """Returns true if callable has first parameter named `ls` or type of annotation""" try: sig = inspect.signature(f) first_p = next(itertools.islice(sig.parameters.values(), 0, 1)) return first_p.name == PARAM_LS or get_type_hints(f)[first_p.name] == annotation except Exception: return False def is_thread_function(f): return getattr(f, ATTR_EXECUTE_IN_THREAD, False) def wrap_with_server(f, server): """Returns a new callable/coroutine with server as first argument.""" if not has_ls_param_or_annotation(f, type(server)): return f if asyncio.iscoroutinefunction(f): async def wrapped(*args, **kwargs): return await f(server, *args, **kwargs) else: wrapped = functools.partial(f, server) if is_thread_function(f): assign_thread_attr(wrapped) return wrapped class FeatureManager: """A class for managing server features. Attributes: _builtin_features(dict): Predefined set of lsp methods _feature_options(dict): Registered feature's options _features(dict): Registered features _commands(dict): Registered commands server(LanguageServer): Reference to the language server If passed, server will be passed to registered features/commands with first parameter: 1. ls - parameter naming convention 2. name: LanguageServer - add typings """ def __init__(self, server=None, converter=None): self._builtin_features = {} self._feature_options = {} self._features = {} self._commands = {} self.server = server self.converter = converter def add_builtin_feature(self, feature_name: str, func: Callable) -> None: """Registers builtin (predefined) feature.""" self._builtin_features[feature_name] = func logger.info("Registered builtin feature %s", feature_name) @property def builtin_features(self) -> Dict: """Returns server builtin features.""" return self._builtin_features def command(self, command_name: str) -> Callable: """Decorator used to register custom commands. Example: @ls.command('myCustomCommand') """ def decorator(f): # Validate if command_name is None or command_name.strip() == "": logger.error("Missing command name.") raise ValidationError("Command name is required.") # Check if not already registered if command_name in self._commands: logger.error('Command "%s" is already registered.', command_name) raise CommandAlreadyRegisteredError(command_name) assign_help_attrs(f, command_name, ATTR_COMMAND_TYPE) wrapped = wrap_with_server(f, self.server) # Assign help attributes for thread decorator assign_help_attrs(wrapped, command_name, ATTR_COMMAND_TYPE) self._commands[command_name] = wrapped logger.info('Command "%s" is successfully registered.', command_name) return f return decorator @property def commands(self) -> Dict: """Returns registered custom commands.""" return self._commands def feature( self, feature_name: str, options: Optional[Any] = None, ) -> Callable: """Decorator used to register LSP features. Example: @ls.feature('textDocument/completion', CompletionItems(trigger_characters=['.'])) """ def decorator(f): # Validate if feature_name is None or feature_name.strip() == "": logger.error("Missing feature name.") raise ValidationError("Feature name is required.") # Add feature if not exists if feature_name in self._features: logger.error('Feature "%s" is already registered.', feature_name) raise FeatureAlreadyRegisteredError(feature_name) assign_help_attrs(f, feature_name, ATTR_FEATURE_TYPE) wrapped = wrap_with_server(f, self.server) # Assign help attributes for thread decorator assign_help_attrs(wrapped, feature_name, ATTR_FEATURE_TYPE) self._features[feature_name] = wrapped if options: options_type = get_method_options_type(feature_name) if options_type and not is_instance( self.converter, options, options_type ): raise TypeError( ( f'Options of method "{feature_name}"' f" should be instance of type {options_type}" ) ) self._feature_options[feature_name] = options logger.info('Registered "%s" with options "%s"', feature_name, options) return f return decorator @property def feature_options(self) -> Dict: """Returns feature options for registered features.""" return self._feature_options @property def features(self) -> Dict: """Returns registered features""" return self._features def thread(self) -> Callable: """Decorator that mark function to execute it in a thread.""" def decorator(f): if asyncio.iscoroutinefunction(f): raise ThreadDecoratorError( f'Thread decorator cannot be used with async functions "{f.__name__}"' ) # Allow any decorator order try: reg_name = getattr(f, ATTR_REGISTERED_NAME) reg_type = getattr(f, ATTR_REGISTERED_TYPE) if reg_type is ATTR_FEATURE_TYPE: assign_thread_attr(self.features[reg_name]) elif reg_type is ATTR_COMMAND_TYPE: assign_thread_attr(self.commands[reg_name]) except AttributeError: assign_thread_attr(f) return f return decorator pygls-1.3.0/pygls/lsp/000077500000000000000000000000001455570166200146205ustar00rootroot00000000000000pygls-1.3.0/pygls/lsp/__init__.py000066400000000000000000000121641455570166200167350ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import cattrs from typing import Any, Callable, List, Optional, Union from lsprotocol.types import ( ALL_TYPES_MAP, METHOD_TO_TYPES, TEXT_DOCUMENT_DID_SAVE, TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, WORKSPACE_DID_CREATE_FILES, WORKSPACE_DID_DELETE_FILES, WORKSPACE_DID_RENAME_FILES, WORKSPACE_WILL_CREATE_FILES, WORKSPACE_WILL_DELETE_FILES, WORKSPACE_WILL_RENAME_FILES, FileOperationRegistrationOptions, SaveOptions, SemanticTokensLegend, SemanticTokensRegistrationOptions, ShowDocumentResult, ) from pygls.exceptions import MethodTypeNotRegisteredError ConfigCallbackType = Callable[[List[Any]], None] ShowDocumentCallbackType = Callable[[ShowDocumentResult], None] METHOD_TO_OPTIONS = { TEXT_DOCUMENT_DID_SAVE: SaveOptions, TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: Union[ SemanticTokensLegend, SemanticTokensRegistrationOptions ], TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: Union[ SemanticTokensLegend, SemanticTokensRegistrationOptions ], TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: Union[ SemanticTokensLegend, SemanticTokensRegistrationOptions ], WORKSPACE_DID_CREATE_FILES: FileOperationRegistrationOptions, WORKSPACE_DID_DELETE_FILES: FileOperationRegistrationOptions, WORKSPACE_DID_RENAME_FILES: FileOperationRegistrationOptions, WORKSPACE_WILL_CREATE_FILES: FileOperationRegistrationOptions, WORKSPACE_WILL_DELETE_FILES: FileOperationRegistrationOptions, WORKSPACE_WILL_RENAME_FILES: FileOperationRegistrationOptions, } def get_method_registration_options_type( method_name, lsp_methods_map=METHOD_TO_TYPES ) -> Optional[Any]: """The type corresponding with a method's options when dynamically registering capability for it.""" try: return lsp_methods_map[method_name][3] except KeyError: raise MethodTypeNotRegisteredError(method_name) def get_method_options_type( method_name, lsp_options_map=METHOD_TO_OPTIONS, lsp_methods_map=METHOD_TO_TYPES ) -> Optional[Any]: """Return the type corresponding with a method's ``ServerCapabilities`` fields. In the majority of cases this simply means returning the ``Options`` type, which we can easily derive from the method's ``RegistrationOptions`` type. However, where the options are more involved (such as semantic tokens) and ``pygls`` does some extra work to help derive the options for the user the type has to be provided via the ``lsp_options_map`` Arguments: method_name: The lsp method name to retrieve the options for lsp_options_map: The map used to override the default options type finding behavior lsp_methods_map: The standard map used to look up the various method types. """ options_type = lsp_options_map.get(method_name, None) if options_type is not None: return options_type registration_type = get_method_registration_options_type( method_name, lsp_methods_map ) if registration_type is None: return None type_name = registration_type.__name__.replace("Registration", "") options_type = ALL_TYPES_MAP.get(type_name, None) if options_type is None: raise MethodTypeNotRegisteredError(method_name) return options_type def get_method_params_type(method_name, lsp_methods_map=METHOD_TO_TYPES): try: return lsp_methods_map[method_name][2] except KeyError: raise MethodTypeNotRegisteredError(method_name) def get_method_return_type(method_name, lsp_methods_map=METHOD_TO_TYPES): try: return lsp_methods_map[method_name][1] except KeyError: raise MethodTypeNotRegisteredError(method_name) def is_instance(cv: cattrs.Converter, o, t): try: cv.unstructure(o, t) return True except TypeError: return False pygls-1.3.0/pygls/lsp/client.py000066400000000000000000002251061455570166200164560ustar00rootroot00000000000000# GENERATED FROM scripts/gen-client.py -- DO NOT EDIT # flake8: noqa from concurrent.futures import Future from lsprotocol import types from pygls.client import JsonRPCClient from pygls.protocol import LanguageServerProtocol from pygls.protocol import default_converter from typing import Any from typing import Callable from typing import List from typing import Optional from typing import Union class BaseLanguageClient(JsonRPCClient): def __init__( self, name: str, version: str, protocol_cls=LanguageServerProtocol, converter_factory=default_converter, **kwargs, ): self.name = name self.version = version super().__init__(protocol_cls, converter_factory, **kwargs) def call_hierarchy_incoming_calls( self, params: types.CallHierarchyIncomingCallsParams, callback: Optional[Callable[[Optional[List[types.CallHierarchyIncomingCall]]], None]] = None, ) -> Future: """Make a :lsp:`callHierarchy/incomingCalls` request. A request to resolve the incoming calls for a given `CallHierarchyItem`. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("callHierarchy/incomingCalls", params, callback) async def call_hierarchy_incoming_calls_async( self, params: types.CallHierarchyIncomingCallsParams, ) -> Optional[List[types.CallHierarchyIncomingCall]]: """Make a :lsp:`callHierarchy/incomingCalls` request. A request to resolve the incoming calls for a given `CallHierarchyItem`. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("callHierarchy/incomingCalls", params) def call_hierarchy_outgoing_calls( self, params: types.CallHierarchyOutgoingCallsParams, callback: Optional[Callable[[Optional[List[types.CallHierarchyOutgoingCall]]], None]] = None, ) -> Future: """Make a :lsp:`callHierarchy/outgoingCalls` request. A request to resolve the outgoing calls for a given `CallHierarchyItem`. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("callHierarchy/outgoingCalls", params, callback) async def call_hierarchy_outgoing_calls_async( self, params: types.CallHierarchyOutgoingCallsParams, ) -> Optional[List[types.CallHierarchyOutgoingCall]]: """Make a :lsp:`callHierarchy/outgoingCalls` request. A request to resolve the outgoing calls for a given `CallHierarchyItem`. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("callHierarchy/outgoingCalls", params) def code_action_resolve( self, params: types.CodeAction, callback: Optional[Callable[[types.CodeAction], None]] = None, ) -> Future: """Make a :lsp:`codeAction/resolve` request. Request to resolve additional information for a given code action.The request's parameter is of type {@link CodeAction} the response is of type {@link CodeAction} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("codeAction/resolve", params, callback) async def code_action_resolve_async( self, params: types.CodeAction, ) -> types.CodeAction: """Make a :lsp:`codeAction/resolve` request. Request to resolve additional information for a given code action.The request's parameter is of type {@link CodeAction} the response is of type {@link CodeAction} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("codeAction/resolve", params) def code_lens_resolve( self, params: types.CodeLens, callback: Optional[Callable[[types.CodeLens], None]] = None, ) -> Future: """Make a :lsp:`codeLens/resolve` request. A request to resolve a command for a given code lens. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("codeLens/resolve", params, callback) async def code_lens_resolve_async( self, params: types.CodeLens, ) -> types.CodeLens: """Make a :lsp:`codeLens/resolve` request. A request to resolve a command for a given code lens. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("codeLens/resolve", params) def completion_item_resolve( self, params: types.CompletionItem, callback: Optional[Callable[[types.CompletionItem], None]] = None, ) -> Future: """Make a :lsp:`completionItem/resolve` request. Request to resolve additional information for a given completion item.The request's parameter is of type {@link CompletionItem} the response is of type {@link CompletionItem} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("completionItem/resolve", params, callback) async def completion_item_resolve_async( self, params: types.CompletionItem, ) -> types.CompletionItem: """Make a :lsp:`completionItem/resolve` request. Request to resolve additional information for a given completion item.The request's parameter is of type {@link CompletionItem} the response is of type {@link CompletionItem} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("completionItem/resolve", params) def document_link_resolve( self, params: types.DocumentLink, callback: Optional[Callable[[types.DocumentLink], None]] = None, ) -> Future: """Make a :lsp:`documentLink/resolve` request. Request to resolve additional information for a given document link. The request's parameter is of type {@link DocumentLink} the response is of type {@link DocumentLink} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("documentLink/resolve", params, callback) async def document_link_resolve_async( self, params: types.DocumentLink, ) -> types.DocumentLink: """Make a :lsp:`documentLink/resolve` request. Request to resolve additional information for a given document link. The request's parameter is of type {@link DocumentLink} the response is of type {@link DocumentLink} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("documentLink/resolve", params) def initialize( self, params: types.InitializeParams, callback: Optional[Callable[[types.InitializeResult], None]] = None, ) -> Future: """Make a :lsp:`initialize` request. The initialize request is sent from the client to the server. It is sent once as the request after starting up the server. The requests parameter is of type {@link InitializeParams} the response if of type {@link InitializeResult} of a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("initialize", params, callback) async def initialize_async( self, params: types.InitializeParams, ) -> types.InitializeResult: """Make a :lsp:`initialize` request. The initialize request is sent from the client to the server. It is sent once as the request after starting up the server. The requests parameter is of type {@link InitializeParams} the response if of type {@link InitializeResult} of a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("initialize", params) def inlay_hint_resolve( self, params: types.InlayHint, callback: Optional[Callable[[types.InlayHint], None]] = None, ) -> Future: """Make a :lsp:`inlayHint/resolve` request. A request to resolve additional properties for an inlay hint. The request's parameter is of type {@link InlayHint}, the response is of type {@link InlayHint} or a Thenable that resolves to such. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("inlayHint/resolve", params, callback) async def inlay_hint_resolve_async( self, params: types.InlayHint, ) -> types.InlayHint: """Make a :lsp:`inlayHint/resolve` request. A request to resolve additional properties for an inlay hint. The request's parameter is of type {@link InlayHint}, the response is of type {@link InlayHint} or a Thenable that resolves to such. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("inlayHint/resolve", params) def shutdown( self, params: None, callback: Optional[Callable[[None], None]] = None, ) -> Future: """Make a :lsp:`shutdown` request. A shutdown request is sent from the client to the server. It is sent once when the client decides to shutdown the server. The only notification that is sent after a shutdown request is the exit event. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("shutdown", params, callback) async def shutdown_async( self, params: None, ) -> None: """Make a :lsp:`shutdown` request. A shutdown request is sent from the client to the server. It is sent once when the client decides to shutdown the server. The only notification that is sent after a shutdown request is the exit event. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("shutdown", params) def text_document_code_action( self, params: types.CodeActionParams, callback: Optional[Callable[[Optional[List[Union[types.Command, types.CodeAction]]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/codeAction` request. A request to provide commands for the given text document and range. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/codeAction", params, callback) async def text_document_code_action_async( self, params: types.CodeActionParams, ) -> Optional[List[Union[types.Command, types.CodeAction]]]: """Make a :lsp:`textDocument/codeAction` request. A request to provide commands for the given text document and range. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/codeAction", params) def text_document_code_lens( self, params: types.CodeLensParams, callback: Optional[Callable[[Optional[List[types.CodeLens]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/codeLens` request. A request to provide code lens for the given text document. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/codeLens", params, callback) async def text_document_code_lens_async( self, params: types.CodeLensParams, ) -> Optional[List[types.CodeLens]]: """Make a :lsp:`textDocument/codeLens` request. A request to provide code lens for the given text document. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/codeLens", params) def text_document_color_presentation( self, params: types.ColorPresentationParams, callback: Optional[Callable[[List[types.ColorPresentation]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/colorPresentation` request. A request to list all presentation for a color. The request's parameter is of type {@link ColorPresentationParams} the response is of type {@link ColorInformation ColorInformation[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/colorPresentation", params, callback) async def text_document_color_presentation_async( self, params: types.ColorPresentationParams, ) -> List[types.ColorPresentation]: """Make a :lsp:`textDocument/colorPresentation` request. A request to list all presentation for a color. The request's parameter is of type {@link ColorPresentationParams} the response is of type {@link ColorInformation ColorInformation[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/colorPresentation", params) def text_document_completion( self, params: types.CompletionParams, callback: Optional[Callable[[Union[List[types.CompletionItem], types.CompletionList, None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/completion` request. Request to request completion at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of type {@link CompletionItem CompletionItem[]} or {@link CompletionList} or a Thenable that resolves to such. The request can delay the computation of the {@link CompletionItem.detail `detail`} and {@link CompletionItem.documentation `documentation`} properties to the `completionItem/resolve` request. However, properties that are needed for the initial sorting and filtering, like `sortText`, `filterText`, `insertText`, and `textEdit`, must not be changed during resolve. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/completion", params, callback) async def text_document_completion_async( self, params: types.CompletionParams, ) -> Union[List[types.CompletionItem], types.CompletionList, None]: """Make a :lsp:`textDocument/completion` request. Request to request completion at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of type {@link CompletionItem CompletionItem[]} or {@link CompletionList} or a Thenable that resolves to such. The request can delay the computation of the {@link CompletionItem.detail `detail`} and {@link CompletionItem.documentation `documentation`} properties to the `completionItem/resolve` request. However, properties that are needed for the initial sorting and filtering, like `sortText`, `filterText`, `insertText`, and `textEdit`, must not be changed during resolve. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/completion", params) def text_document_declaration( self, params: types.DeclarationParams, callback: Optional[Callable[[Union[types.Location, List[types.Location], List[types.LocationLink], None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/declaration` request. A request to resolve the type definition locations of a symbol at a given text document position. The request's parameter is of type {@link TextDocumentPositionParams} the response is of type {@link Declaration} or a typed array of {@link DeclarationLink} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/declaration", params, callback) async def text_document_declaration_async( self, params: types.DeclarationParams, ) -> Union[types.Location, List[types.Location], List[types.LocationLink], None]: """Make a :lsp:`textDocument/declaration` request. A request to resolve the type definition locations of a symbol at a given text document position. The request's parameter is of type {@link TextDocumentPositionParams} the response is of type {@link Declaration} or a typed array of {@link DeclarationLink} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/declaration", params) def text_document_definition( self, params: types.DefinitionParams, callback: Optional[Callable[[Union[types.Location, List[types.Location], List[types.LocationLink], None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/definition` request. A request to resolve the definition location of a symbol at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of either type {@link Definition} or a typed array of {@link DefinitionLink} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/definition", params, callback) async def text_document_definition_async( self, params: types.DefinitionParams, ) -> Union[types.Location, List[types.Location], List[types.LocationLink], None]: """Make a :lsp:`textDocument/definition` request. A request to resolve the definition location of a symbol at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of either type {@link Definition} or a typed array of {@link DefinitionLink} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/definition", params) def text_document_diagnostic( self, params: types.DocumentDiagnosticParams, callback: Optional[Callable[[Union[types.RelatedFullDocumentDiagnosticReport, types.RelatedUnchangedDocumentDiagnosticReport]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/diagnostic` request. The document diagnostic request definition. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/diagnostic", params, callback) async def text_document_diagnostic_async( self, params: types.DocumentDiagnosticParams, ) -> Union[types.RelatedFullDocumentDiagnosticReport, types.RelatedUnchangedDocumentDiagnosticReport]: """Make a :lsp:`textDocument/diagnostic` request. The document diagnostic request definition. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/diagnostic", params) def text_document_document_color( self, params: types.DocumentColorParams, callback: Optional[Callable[[List[types.ColorInformation]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/documentColor` request. A request to list all color symbols found in a given text document. The request's parameter is of type {@link DocumentColorParams} the response is of type {@link ColorInformation ColorInformation[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/documentColor", params, callback) async def text_document_document_color_async( self, params: types.DocumentColorParams, ) -> List[types.ColorInformation]: """Make a :lsp:`textDocument/documentColor` request. A request to list all color symbols found in a given text document. The request's parameter is of type {@link DocumentColorParams} the response is of type {@link ColorInformation ColorInformation[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/documentColor", params) def text_document_document_highlight( self, params: types.DocumentHighlightParams, callback: Optional[Callable[[Optional[List[types.DocumentHighlight]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/documentHighlight` request. Request to resolve a {@link DocumentHighlight} for a given text document position. The request's parameter is of type {@link TextDocumentPosition} the request response is an array of type {@link DocumentHighlight} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/documentHighlight", params, callback) async def text_document_document_highlight_async( self, params: types.DocumentHighlightParams, ) -> Optional[List[types.DocumentHighlight]]: """Make a :lsp:`textDocument/documentHighlight` request. Request to resolve a {@link DocumentHighlight} for a given text document position. The request's parameter is of type {@link TextDocumentPosition} the request response is an array of type {@link DocumentHighlight} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/documentHighlight", params) def text_document_document_link( self, params: types.DocumentLinkParams, callback: Optional[Callable[[Optional[List[types.DocumentLink]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/documentLink` request. A request to provide document links """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/documentLink", params, callback) async def text_document_document_link_async( self, params: types.DocumentLinkParams, ) -> Optional[List[types.DocumentLink]]: """Make a :lsp:`textDocument/documentLink` request. A request to provide document links """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/documentLink", params) def text_document_document_symbol( self, params: types.DocumentSymbolParams, callback: Optional[Callable[[Union[List[types.SymbolInformation], List[types.DocumentSymbol], None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/documentSymbol` request. A request to list all symbols found in a given text document. The request's parameter is of type {@link TextDocumentIdentifier} the response is of type {@link SymbolInformation SymbolInformation[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/documentSymbol", params, callback) async def text_document_document_symbol_async( self, params: types.DocumentSymbolParams, ) -> Union[List[types.SymbolInformation], List[types.DocumentSymbol], None]: """Make a :lsp:`textDocument/documentSymbol` request. A request to list all symbols found in a given text document. The request's parameter is of type {@link TextDocumentIdentifier} the response is of type {@link SymbolInformation SymbolInformation[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/documentSymbol", params) def text_document_folding_range( self, params: types.FoldingRangeParams, callback: Optional[Callable[[Optional[List[types.FoldingRange]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/foldingRange` request. A request to provide folding ranges in a document. The request's parameter is of type {@link FoldingRangeParams}, the response is of type {@link FoldingRangeList} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/foldingRange", params, callback) async def text_document_folding_range_async( self, params: types.FoldingRangeParams, ) -> Optional[List[types.FoldingRange]]: """Make a :lsp:`textDocument/foldingRange` request. A request to provide folding ranges in a document. The request's parameter is of type {@link FoldingRangeParams}, the response is of type {@link FoldingRangeList} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/foldingRange", params) def text_document_formatting( self, params: types.DocumentFormattingParams, callback: Optional[Callable[[Optional[List[types.TextEdit]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/formatting` request. A request to format a whole document. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/formatting", params, callback) async def text_document_formatting_async( self, params: types.DocumentFormattingParams, ) -> Optional[List[types.TextEdit]]: """Make a :lsp:`textDocument/formatting` request. A request to format a whole document. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/formatting", params) def text_document_hover( self, params: types.HoverParams, callback: Optional[Callable[[Optional[types.Hover]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/hover` request. Request to request hover information at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of type {@link Hover} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/hover", params, callback) async def text_document_hover_async( self, params: types.HoverParams, ) -> Optional[types.Hover]: """Make a :lsp:`textDocument/hover` request. Request to request hover information at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of type {@link Hover} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/hover", params) def text_document_implementation( self, params: types.ImplementationParams, callback: Optional[Callable[[Union[types.Location, List[types.Location], List[types.LocationLink], None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/implementation` request. A request to resolve the implementation locations of a symbol at a given text document position. The request's parameter is of type {@link TextDocumentPositionParams} the response is of type {@link Definition} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/implementation", params, callback) async def text_document_implementation_async( self, params: types.ImplementationParams, ) -> Union[types.Location, List[types.Location], List[types.LocationLink], None]: """Make a :lsp:`textDocument/implementation` request. A request to resolve the implementation locations of a symbol at a given text document position. The request's parameter is of type {@link TextDocumentPositionParams} the response is of type {@link Definition} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/implementation", params) def text_document_inlay_hint( self, params: types.InlayHintParams, callback: Optional[Callable[[Optional[List[types.InlayHint]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/inlayHint` request. A request to provide inlay hints in a document. The request's parameter is of type {@link InlayHintsParams}, the response is of type {@link InlayHint InlayHint[]} or a Thenable that resolves to such. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/inlayHint", params, callback) async def text_document_inlay_hint_async( self, params: types.InlayHintParams, ) -> Optional[List[types.InlayHint]]: """Make a :lsp:`textDocument/inlayHint` request. A request to provide inlay hints in a document. The request's parameter is of type {@link InlayHintsParams}, the response is of type {@link InlayHint InlayHint[]} or a Thenable that resolves to such. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/inlayHint", params) def text_document_inline_completion( self, params: types.InlineCompletionParams, callback: Optional[Callable[[Union[types.InlineCompletionList, List[types.InlineCompletionItem], None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/inlineCompletion` request. A request to provide inline completions in a document. The request's parameter is of type {@link InlineCompletionParams}, the response is of type {@link InlineCompletion InlineCompletion[]} or a Thenable that resolves to such. @since 3.18.0 @proposed """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/inlineCompletion", params, callback) async def text_document_inline_completion_async( self, params: types.InlineCompletionParams, ) -> Union[types.InlineCompletionList, List[types.InlineCompletionItem], None]: """Make a :lsp:`textDocument/inlineCompletion` request. A request to provide inline completions in a document. The request's parameter is of type {@link InlineCompletionParams}, the response is of type {@link InlineCompletion InlineCompletion[]} or a Thenable that resolves to such. @since 3.18.0 @proposed """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/inlineCompletion", params) def text_document_inline_value( self, params: types.InlineValueParams, callback: Optional[Callable[[Optional[List[Union[types.InlineValueText, types.InlineValueVariableLookup, types.InlineValueEvaluatableExpression]]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/inlineValue` request. A request to provide inline values in a document. The request's parameter is of type {@link InlineValueParams}, the response is of type {@link InlineValue InlineValue[]} or a Thenable that resolves to such. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/inlineValue", params, callback) async def text_document_inline_value_async( self, params: types.InlineValueParams, ) -> Optional[List[Union[types.InlineValueText, types.InlineValueVariableLookup, types.InlineValueEvaluatableExpression]]]: """Make a :lsp:`textDocument/inlineValue` request. A request to provide inline values in a document. The request's parameter is of type {@link InlineValueParams}, the response is of type {@link InlineValue InlineValue[]} or a Thenable that resolves to such. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/inlineValue", params) def text_document_linked_editing_range( self, params: types.LinkedEditingRangeParams, callback: Optional[Callable[[Optional[types.LinkedEditingRanges]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/linkedEditingRange` request. A request to provide ranges that can be edited together. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/linkedEditingRange", params, callback) async def text_document_linked_editing_range_async( self, params: types.LinkedEditingRangeParams, ) -> Optional[types.LinkedEditingRanges]: """Make a :lsp:`textDocument/linkedEditingRange` request. A request to provide ranges that can be edited together. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/linkedEditingRange", params) def text_document_moniker( self, params: types.MonikerParams, callback: Optional[Callable[[Optional[List[types.Moniker]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/moniker` request. A request to get the moniker of a symbol at a given text document position. The request parameter is of type {@link TextDocumentPositionParams}. The response is of type {@link Moniker Moniker[]} or `null`. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/moniker", params, callback) async def text_document_moniker_async( self, params: types.MonikerParams, ) -> Optional[List[types.Moniker]]: """Make a :lsp:`textDocument/moniker` request. A request to get the moniker of a symbol at a given text document position. The request parameter is of type {@link TextDocumentPositionParams}. The response is of type {@link Moniker Moniker[]} or `null`. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/moniker", params) def text_document_on_type_formatting( self, params: types.DocumentOnTypeFormattingParams, callback: Optional[Callable[[Optional[List[types.TextEdit]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/onTypeFormatting` request. A request to format a document on type. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/onTypeFormatting", params, callback) async def text_document_on_type_formatting_async( self, params: types.DocumentOnTypeFormattingParams, ) -> Optional[List[types.TextEdit]]: """Make a :lsp:`textDocument/onTypeFormatting` request. A request to format a document on type. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/onTypeFormatting", params) def text_document_prepare_call_hierarchy( self, params: types.CallHierarchyPrepareParams, callback: Optional[Callable[[Optional[List[types.CallHierarchyItem]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/prepareCallHierarchy` request. A request to result a `CallHierarchyItem` in a document at a given position. Can be used as an input to an incoming or outgoing call hierarchy. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/prepareCallHierarchy", params, callback) async def text_document_prepare_call_hierarchy_async( self, params: types.CallHierarchyPrepareParams, ) -> Optional[List[types.CallHierarchyItem]]: """Make a :lsp:`textDocument/prepareCallHierarchy` request. A request to result a `CallHierarchyItem` in a document at a given position. Can be used as an input to an incoming or outgoing call hierarchy. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/prepareCallHierarchy", params) def text_document_prepare_rename( self, params: types.PrepareRenameParams, callback: Optional[Callable[[Union[types.Range, types.PrepareRenameResult_Type1, types.PrepareRenameResult_Type2, None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/prepareRename` request. A request to test and perform the setup necessary for a rename. @since 3.16 - support for default behavior """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/prepareRename", params, callback) async def text_document_prepare_rename_async( self, params: types.PrepareRenameParams, ) -> Union[types.Range, types.PrepareRenameResult_Type1, types.PrepareRenameResult_Type2, None]: """Make a :lsp:`textDocument/prepareRename` request. A request to test and perform the setup necessary for a rename. @since 3.16 - support for default behavior """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/prepareRename", params) def text_document_prepare_type_hierarchy( self, params: types.TypeHierarchyPrepareParams, callback: Optional[Callable[[Optional[List[types.TypeHierarchyItem]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/prepareTypeHierarchy` request. A request to result a `TypeHierarchyItem` in a document at a given position. Can be used as an input to a subtypes or supertypes type hierarchy. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/prepareTypeHierarchy", params, callback) async def text_document_prepare_type_hierarchy_async( self, params: types.TypeHierarchyPrepareParams, ) -> Optional[List[types.TypeHierarchyItem]]: """Make a :lsp:`textDocument/prepareTypeHierarchy` request. A request to result a `TypeHierarchyItem` in a document at a given position. Can be used as an input to a subtypes or supertypes type hierarchy. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/prepareTypeHierarchy", params) def text_document_ranges_formatting( self, params: types.DocumentRangesFormattingParams, callback: Optional[Callable[[Optional[List[types.TextEdit]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/rangesFormatting` request. A request to format ranges in a document. @since 3.18.0 @proposed """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/rangesFormatting", params, callback) async def text_document_ranges_formatting_async( self, params: types.DocumentRangesFormattingParams, ) -> Optional[List[types.TextEdit]]: """Make a :lsp:`textDocument/rangesFormatting` request. A request to format ranges in a document. @since 3.18.0 @proposed """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/rangesFormatting", params) def text_document_range_formatting( self, params: types.DocumentRangeFormattingParams, callback: Optional[Callable[[Optional[List[types.TextEdit]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/rangeFormatting` request. A request to format a range in a document. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/rangeFormatting", params, callback) async def text_document_range_formatting_async( self, params: types.DocumentRangeFormattingParams, ) -> Optional[List[types.TextEdit]]: """Make a :lsp:`textDocument/rangeFormatting` request. A request to format a range in a document. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/rangeFormatting", params) def text_document_references( self, params: types.ReferenceParams, callback: Optional[Callable[[Optional[List[types.Location]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/references` request. A request to resolve project-wide references for the symbol denoted by the given text document position. The request's parameter is of type {@link ReferenceParams} the response is of type {@link Location Location[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/references", params, callback) async def text_document_references_async( self, params: types.ReferenceParams, ) -> Optional[List[types.Location]]: """Make a :lsp:`textDocument/references` request. A request to resolve project-wide references for the symbol denoted by the given text document position. The request's parameter is of type {@link ReferenceParams} the response is of type {@link Location Location[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/references", params) def text_document_rename( self, params: types.RenameParams, callback: Optional[Callable[[Optional[types.WorkspaceEdit]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/rename` request. A request to rename a symbol. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/rename", params, callback) async def text_document_rename_async( self, params: types.RenameParams, ) -> Optional[types.WorkspaceEdit]: """Make a :lsp:`textDocument/rename` request. A request to rename a symbol. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/rename", params) def text_document_selection_range( self, params: types.SelectionRangeParams, callback: Optional[Callable[[Optional[List[types.SelectionRange]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/selectionRange` request. A request to provide selection ranges in a document. The request's parameter is of type {@link SelectionRangeParams}, the response is of type {@link SelectionRange SelectionRange[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/selectionRange", params, callback) async def text_document_selection_range_async( self, params: types.SelectionRangeParams, ) -> Optional[List[types.SelectionRange]]: """Make a :lsp:`textDocument/selectionRange` request. A request to provide selection ranges in a document. The request's parameter is of type {@link SelectionRangeParams}, the response is of type {@link SelectionRange SelectionRange[]} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/selectionRange", params) def text_document_semantic_tokens_full( self, params: types.SemanticTokensParams, callback: Optional[Callable[[Optional[types.SemanticTokens]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/semanticTokens/full` request. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/semanticTokens/full", params, callback) async def text_document_semantic_tokens_full_async( self, params: types.SemanticTokensParams, ) -> Optional[types.SemanticTokens]: """Make a :lsp:`textDocument/semanticTokens/full` request. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/semanticTokens/full", params) def text_document_semantic_tokens_full_delta( self, params: types.SemanticTokensDeltaParams, callback: Optional[Callable[[Union[types.SemanticTokens, types.SemanticTokensDelta, None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/semanticTokens/full/delta` request. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/semanticTokens/full/delta", params, callback) async def text_document_semantic_tokens_full_delta_async( self, params: types.SemanticTokensDeltaParams, ) -> Union[types.SemanticTokens, types.SemanticTokensDelta, None]: """Make a :lsp:`textDocument/semanticTokens/full/delta` request. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/semanticTokens/full/delta", params) def text_document_semantic_tokens_range( self, params: types.SemanticTokensRangeParams, callback: Optional[Callable[[Optional[types.SemanticTokens]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/semanticTokens/range` request. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/semanticTokens/range", params, callback) async def text_document_semantic_tokens_range_async( self, params: types.SemanticTokensRangeParams, ) -> Optional[types.SemanticTokens]: """Make a :lsp:`textDocument/semanticTokens/range` request. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/semanticTokens/range", params) def text_document_signature_help( self, params: types.SignatureHelpParams, callback: Optional[Callable[[Optional[types.SignatureHelp]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/signatureHelp` request. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/signatureHelp", params, callback) async def text_document_signature_help_async( self, params: types.SignatureHelpParams, ) -> Optional[types.SignatureHelp]: """Make a :lsp:`textDocument/signatureHelp` request. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/signatureHelp", params) def text_document_type_definition( self, params: types.TypeDefinitionParams, callback: Optional[Callable[[Union[types.Location, List[types.Location], List[types.LocationLink], None]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/typeDefinition` request. A request to resolve the type definition locations of a symbol at a given text document position. The request's parameter is of type {@link TextDocumentPositionParams} the response is of type {@link Definition} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/typeDefinition", params, callback) async def text_document_type_definition_async( self, params: types.TypeDefinitionParams, ) -> Union[types.Location, List[types.Location], List[types.LocationLink], None]: """Make a :lsp:`textDocument/typeDefinition` request. A request to resolve the type definition locations of a symbol at a given text document position. The request's parameter is of type {@link TextDocumentPositionParams} the response is of type {@link Definition} or a Thenable that resolves to such. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/typeDefinition", params) def text_document_will_save_wait_until( self, params: types.WillSaveTextDocumentParams, callback: Optional[Callable[[Optional[List[types.TextEdit]]], None]] = None, ) -> Future: """Make a :lsp:`textDocument/willSaveWaitUntil` request. A document will save request is sent from the client to the server before the document is actually saved. The request can return an array of TextEdits which will be applied to the text document before it is saved. Please note that clients might drop results if computing the text edits took too long or if a server constantly fails on this request. This is done to keep the save fast and reliable. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("textDocument/willSaveWaitUntil", params, callback) async def text_document_will_save_wait_until_async( self, params: types.WillSaveTextDocumentParams, ) -> Optional[List[types.TextEdit]]: """Make a :lsp:`textDocument/willSaveWaitUntil` request. A document will save request is sent from the client to the server before the document is actually saved. The request can return an array of TextEdits which will be applied to the text document before it is saved. Please note that clients might drop results if computing the text edits took too long or if a server constantly fails on this request. This is done to keep the save fast and reliable. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("textDocument/willSaveWaitUntil", params) def type_hierarchy_subtypes( self, params: types.TypeHierarchySubtypesParams, callback: Optional[Callable[[Optional[List[types.TypeHierarchyItem]]], None]] = None, ) -> Future: """Make a :lsp:`typeHierarchy/subtypes` request. A request to resolve the subtypes for a given `TypeHierarchyItem`. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("typeHierarchy/subtypes", params, callback) async def type_hierarchy_subtypes_async( self, params: types.TypeHierarchySubtypesParams, ) -> Optional[List[types.TypeHierarchyItem]]: """Make a :lsp:`typeHierarchy/subtypes` request. A request to resolve the subtypes for a given `TypeHierarchyItem`. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("typeHierarchy/subtypes", params) def type_hierarchy_supertypes( self, params: types.TypeHierarchySupertypesParams, callback: Optional[Callable[[Optional[List[types.TypeHierarchyItem]]], None]] = None, ) -> Future: """Make a :lsp:`typeHierarchy/supertypes` request. A request to resolve the supertypes for a given `TypeHierarchyItem`. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("typeHierarchy/supertypes", params, callback) async def type_hierarchy_supertypes_async( self, params: types.TypeHierarchySupertypesParams, ) -> Optional[List[types.TypeHierarchyItem]]: """Make a :lsp:`typeHierarchy/supertypes` request. A request to resolve the supertypes for a given `TypeHierarchyItem`. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("typeHierarchy/supertypes", params) def workspace_diagnostic( self, params: types.WorkspaceDiagnosticParams, callback: Optional[Callable[[types.WorkspaceDiagnosticReport], None]] = None, ) -> Future: """Make a :lsp:`workspace/diagnostic` request. The workspace diagnostic request definition. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("workspace/diagnostic", params, callback) async def workspace_diagnostic_async( self, params: types.WorkspaceDiagnosticParams, ) -> types.WorkspaceDiagnosticReport: """Make a :lsp:`workspace/diagnostic` request. The workspace diagnostic request definition. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("workspace/diagnostic", params) def workspace_execute_command( self, params: types.ExecuteCommandParams, callback: Optional[Callable[[Optional[Any]], None]] = None, ) -> Future: """Make a :lsp:`workspace/executeCommand` request. A request send from the client to the server to execute a command. The request might return a workspace edit which the client will apply to the workspace. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("workspace/executeCommand", params, callback) async def workspace_execute_command_async( self, params: types.ExecuteCommandParams, ) -> Optional[Any]: """Make a :lsp:`workspace/executeCommand` request. A request send from the client to the server to execute a command. The request might return a workspace edit which the client will apply to the workspace. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("workspace/executeCommand", params) def workspace_symbol( self, params: types.WorkspaceSymbolParams, callback: Optional[Callable[[Union[List[types.SymbolInformation], List[types.WorkspaceSymbol], None]], None]] = None, ) -> Future: """Make a :lsp:`workspace/symbol` request. A request to list project-wide symbols matching the query string given by the {@link WorkspaceSymbolParams}. The response is of type {@link SymbolInformation SymbolInformation[]} or a Thenable that resolves to such. @since 3.17.0 - support for WorkspaceSymbol in the returned data. Clients need to advertise support for WorkspaceSymbols via the client capability `workspace.symbol.resolveSupport`. """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("workspace/symbol", params, callback) async def workspace_symbol_async( self, params: types.WorkspaceSymbolParams, ) -> Union[List[types.SymbolInformation], List[types.WorkspaceSymbol], None]: """Make a :lsp:`workspace/symbol` request. A request to list project-wide symbols matching the query string given by the {@link WorkspaceSymbolParams}. The response is of type {@link SymbolInformation SymbolInformation[]} or a Thenable that resolves to such. @since 3.17.0 - support for WorkspaceSymbol in the returned data. Clients need to advertise support for WorkspaceSymbols via the client capability `workspace.symbol.resolveSupport`. """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("workspace/symbol", params) def workspace_symbol_resolve( self, params: types.WorkspaceSymbol, callback: Optional[Callable[[types.WorkspaceSymbol], None]] = None, ) -> Future: """Make a :lsp:`workspaceSymbol/resolve` request. A request to resolve the range inside the workspace symbol's location. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("workspaceSymbol/resolve", params, callback) async def workspace_symbol_resolve_async( self, params: types.WorkspaceSymbol, ) -> types.WorkspaceSymbol: """Make a :lsp:`workspaceSymbol/resolve` request. A request to resolve the range inside the workspace symbol's location. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("workspaceSymbol/resolve", params) def workspace_will_create_files( self, params: types.CreateFilesParams, callback: Optional[Callable[[Optional[types.WorkspaceEdit]], None]] = None, ) -> Future: """Make a :lsp:`workspace/willCreateFiles` request. The will create files request is sent from the client to the server before files are actually created as long as the creation is triggered from within the client. The request can return a `WorkspaceEdit` which will be applied to workspace before the files are created. Hence the `WorkspaceEdit` can not manipulate the content of the file to be created. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("workspace/willCreateFiles", params, callback) async def workspace_will_create_files_async( self, params: types.CreateFilesParams, ) -> Optional[types.WorkspaceEdit]: """Make a :lsp:`workspace/willCreateFiles` request. The will create files request is sent from the client to the server before files are actually created as long as the creation is triggered from within the client. The request can return a `WorkspaceEdit` which will be applied to workspace before the files are created. Hence the `WorkspaceEdit` can not manipulate the content of the file to be created. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("workspace/willCreateFiles", params) def workspace_will_delete_files( self, params: types.DeleteFilesParams, callback: Optional[Callable[[Optional[types.WorkspaceEdit]], None]] = None, ) -> Future: """Make a :lsp:`workspace/willDeleteFiles` request. The did delete files notification is sent from the client to the server when files were deleted from within the client. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("workspace/willDeleteFiles", params, callback) async def workspace_will_delete_files_async( self, params: types.DeleteFilesParams, ) -> Optional[types.WorkspaceEdit]: """Make a :lsp:`workspace/willDeleteFiles` request. The did delete files notification is sent from the client to the server when files were deleted from within the client. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("workspace/willDeleteFiles", params) def workspace_will_rename_files( self, params: types.RenameFilesParams, callback: Optional[Callable[[Optional[types.WorkspaceEdit]], None]] = None, ) -> Future: """Make a :lsp:`workspace/willRenameFiles` request. The will rename files request is sent from the client to the server before files are actually renamed as long as the rename is triggered from within the client. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return self.protocol.send_request("workspace/willRenameFiles", params, callback) async def workspace_will_rename_files_async( self, params: types.RenameFilesParams, ) -> Optional[types.WorkspaceEdit]: """Make a :lsp:`workspace/willRenameFiles` request. The will rename files request is sent from the client to the server before files are actually renamed as long as the rename is triggered from within the client. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") return await self.protocol.send_request_async("workspace/willRenameFiles", params) def cancel_request(self, params: types.CancelParams) -> None: """Send a :lsp:`$/cancelRequest` notification. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("$/cancelRequest", params) def exit(self, params: None) -> None: """Send a :lsp:`exit` notification. The exit event is sent from the client to the server to ask the server to exit its process. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("exit", params) def initialized(self, params: types.InitializedParams) -> None: """Send a :lsp:`initialized` notification. The initialized notification is sent from the client to the server after the client is fully initialized and the server is allowed to send requests from the server to the client. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("initialized", params) def notebook_document_did_change(self, params: types.DidChangeNotebookDocumentParams) -> None: """Send a :lsp:`notebookDocument/didChange` notification. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("notebookDocument/didChange", params) def notebook_document_did_close(self, params: types.DidCloseNotebookDocumentParams) -> None: """Send a :lsp:`notebookDocument/didClose` notification. A notification sent when a notebook closes. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("notebookDocument/didClose", params) def notebook_document_did_open(self, params: types.DidOpenNotebookDocumentParams) -> None: """Send a :lsp:`notebookDocument/didOpen` notification. A notification sent when a notebook opens. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("notebookDocument/didOpen", params) def notebook_document_did_save(self, params: types.DidSaveNotebookDocumentParams) -> None: """Send a :lsp:`notebookDocument/didSave` notification. A notification sent when a notebook document is saved. @since 3.17.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("notebookDocument/didSave", params) def progress(self, params: types.ProgressParams) -> None: """Send a :lsp:`$/progress` notification. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("$/progress", params) def set_trace(self, params: types.SetTraceParams) -> None: """Send a :lsp:`$/setTrace` notification. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("$/setTrace", params) def text_document_did_change(self, params: types.DidChangeTextDocumentParams) -> None: """Send a :lsp:`textDocument/didChange` notification. The document change notification is sent from the client to the server to signal changes to a text document. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("textDocument/didChange", params) def text_document_did_close(self, params: types.DidCloseTextDocumentParams) -> None: """Send a :lsp:`textDocument/didClose` notification. The document close notification is sent from the client to the server when the document got closed in the client. The document's truth now exists where the document's uri points to (e.g. if the document's uri is a file uri the truth now exists on disk). As with the open notification the close notification is about managing the document's content. Receiving a close notification doesn't mean that the document was open in an editor before. A close notification requires a previous open notification to be sent. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("textDocument/didClose", params) def text_document_did_open(self, params: types.DidOpenTextDocumentParams) -> None: """Send a :lsp:`textDocument/didOpen` notification. The document open notification is sent from the client to the server to signal newly opened text documents. The document's truth is now managed by the client and the server must not try to read the document's truth using the document's uri. Open in this sense means it is managed by the client. It doesn't necessarily mean that its content is presented in an editor. An open notification must not be sent more than once without a corresponding close notification send before. This means open and close notification must be balanced and the max open count is one. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("textDocument/didOpen", params) def text_document_did_save(self, params: types.DidSaveTextDocumentParams) -> None: """Send a :lsp:`textDocument/didSave` notification. The document save notification is sent from the client to the server when the document got saved in the client. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("textDocument/didSave", params) def text_document_will_save(self, params: types.WillSaveTextDocumentParams) -> None: """Send a :lsp:`textDocument/willSave` notification. A document will save notification is sent from the client to the server before the document is actually saved. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("textDocument/willSave", params) def window_work_done_progress_cancel(self, params: types.WorkDoneProgressCancelParams) -> None: """Send a :lsp:`window/workDoneProgress/cancel` notification. The `window/workDoneProgress/cancel` notification is sent from the client to the server to cancel a progress initiated on the server side. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("window/workDoneProgress/cancel", params) def workspace_did_change_configuration(self, params: types.DidChangeConfigurationParams) -> None: """Send a :lsp:`workspace/didChangeConfiguration` notification. The configuration change notification is sent from the client to the server when the client's configuration has changed. The notification contains the changed configuration as defined by the language client. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("workspace/didChangeConfiguration", params) def workspace_did_change_watched_files(self, params: types.DidChangeWatchedFilesParams) -> None: """Send a :lsp:`workspace/didChangeWatchedFiles` notification. The watched files notification is sent from the client to the server when the client detects changes to file watched by the language client. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("workspace/didChangeWatchedFiles", params) def workspace_did_change_workspace_folders(self, params: types.DidChangeWorkspaceFoldersParams) -> None: """Send a :lsp:`workspace/didChangeWorkspaceFolders` notification. The `workspace/didChangeWorkspaceFolders` notification is sent from the client to the server when the workspace folder configuration changes. """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("workspace/didChangeWorkspaceFolders", params) def workspace_did_create_files(self, params: types.CreateFilesParams) -> None: """Send a :lsp:`workspace/didCreateFiles` notification. The did create files notification is sent from the client to the server when files were created from within the client. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("workspace/didCreateFiles", params) def workspace_did_delete_files(self, params: types.DeleteFilesParams) -> None: """Send a :lsp:`workspace/didDeleteFiles` notification. The will delete files request is sent from the client to the server before files are actually deleted as long as the deletion is triggered from within the client. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("workspace/didDeleteFiles", params) def workspace_did_rename_files(self, params: types.RenameFilesParams) -> None: """Send a :lsp:`workspace/didRenameFiles` notification. The did rename files notification is sent from the client to the server when files were renamed from within the client. @since 3.16.0 """ if self.stopped: raise RuntimeError("Client has been stopped.") self.protocol.notify("workspace/didRenameFiles", params) pygls-1.3.0/pygls/progress.py000066400000000000000000000053451455570166200162470ustar00rootroot00000000000000import asyncio from concurrent.futures import Future from typing import Dict from lsprotocol.types import ( PROGRESS, WINDOW_WORK_DONE_PROGRESS_CREATE, ProgressParams, ProgressToken, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressReport, WorkDoneProgressCreateParams, ) from pygls.protocol import LanguageServerProtocol class Progress: """A class for working with client's progress bar. Attributes: _lsp(LanguageServerProtocol): Language server protocol instance tokens(dict): Holds futures for work done progress tokens that are already registered. These futures will be cancelled if the client sends a cancel work done process notification. """ def __init__(self, lsp: LanguageServerProtocol) -> None: self._lsp = lsp self.tokens: Dict[ProgressToken, Future] = {} def _check_token_registered(self, token: ProgressToken) -> None: if token in self.tokens: raise Exception("Token is already registered!") def _register_token(self, token: ProgressToken) -> None: self.tokens[token] = Future() def create(self, token: ProgressToken, callback=None) -> Future: """Create a server initiated work done progress.""" self._check_token_registered(token) def on_created(*args, **kwargs): self._register_token(token) if callback is not None: callback(*args, **kwargs) return self._lsp.send_request( WINDOW_WORK_DONE_PROGRESS_CREATE, WorkDoneProgressCreateParams(token=token), on_created, ) async def create_async(self, token: ProgressToken) -> asyncio.Future: """Create a server initiated work done progress.""" self._check_token_registered(token) result = await self._lsp.send_request_async( WINDOW_WORK_DONE_PROGRESS_CREATE, WorkDoneProgressCreateParams(token=token), ) self._register_token(token) return result def begin(self, token: ProgressToken, value: WorkDoneProgressBegin) -> None: """Notify beginning of work.""" # Register cancellation future for the case of client initiated progress self.tokens.setdefault(token, Future()) return self._lsp.notify(PROGRESS, ProgressParams(token=token, value=value)) def report(self, token: ProgressToken, value: WorkDoneProgressReport) -> None: """Notify progress of work.""" self._lsp.notify(PROGRESS, ProgressParams(token=token, value=value)) def end(self, token: ProgressToken, value: WorkDoneProgressEnd) -> None: """Notify end of work.""" self._lsp.notify(PROGRESS, ProgressParams(token=token, value=value)) pygls-1.3.0/pygls/protocol/000077500000000000000000000000001455570166200156635ustar00rootroot00000000000000pygls-1.3.0/pygls/protocol/__init__.py000066400000000000000000000034361455570166200200020ustar00rootroot00000000000000import json from typing import Any from collections import namedtuple from lsprotocol import converters from pygls.protocol.json_rpc import ( JsonRPCNotification, JsonRPCProtocol, JsonRPCRequestMessage, JsonRPCResponseMessage, ) from pygls.protocol.language_server import LanguageServerProtocol, lsp_method from pygls.protocol.lsp_meta import LSPMeta, call_user_feature def _dict_to_object(d: Any): """Create nested objects (namedtuple) from dict.""" if d is None: return None if not isinstance(d, dict): return d type_name = d.pop("type_name", "Object") return json.loads( json.dumps(d), object_hook=lambda p: namedtuple(type_name, p.keys(), rename=True)(*p.values()), ) def _params_field_structure_hook(obj, cls): if "params" in obj: obj["params"] = _dict_to_object(obj["params"]) return cls(**obj) def _result_field_structure_hook(obj, cls): if "result" in obj: obj["result"] = _dict_to_object(obj["result"]) return cls(**obj) def default_converter(): """Default converter factory function.""" converter = converters.get_converter() converter.register_structure_hook( JsonRPCRequestMessage, _params_field_structure_hook ) converter.register_structure_hook( JsonRPCResponseMessage, _result_field_structure_hook ) converter.register_structure_hook(JsonRPCNotification, _params_field_structure_hook) return converter __all__ = ( "JsonRPCProtocol", "LanguageServerProtocol", "JsonRPCRequestMessage", "JsonRPCResponseMessage", "JsonRPCNotification", "LSPMeta", "call_user_feature", "_dict_to_object", "_params_field_structure_hook", "_result_field_structure_hook", "default_converter", "lsp_method", ) pygls-1.3.0/pygls/protocol/json_rpc.py000066400000000000000000000472341455570166200200640ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from __future__ import annotations import asyncio import enum import json import logging import re import sys import uuid import traceback from concurrent.futures import Future from functools import partial from typing import ( Any, Dict, List, Optional, Type, Union, TYPE_CHECKING, ) if TYPE_CHECKING: from pygls.server import LanguageServer, WebSocketTransportAdapter import attrs from cattrs.errors import ClassValidationError from lsprotocol.types import ( CANCEL_REQUEST, EXIT, WORKSPACE_EXECUTE_COMMAND, ResponseError, ResponseErrorMessage, ) from pygls.exceptions import ( JsonRpcException, JsonRpcInternalError, JsonRpcInvalidParams, JsonRpcMethodNotFound, JsonRpcRequestCancelled, FeatureNotificationError, FeatureRequestError, ) from pygls.feature_manager import FeatureManager, is_thread_function logger = logging.getLogger(__name__) @attrs.define class JsonRPCNotification: """A class that represents a generic json rpc notification message. Used as a fallback for unknown types. """ method: str jsonrpc: str params: Any @attrs.define class JsonRPCRequestMessage: """A class that represents a generic json rpc request message. Used as a fallback for unknown types. """ id: Union[int, str] method: str jsonrpc: str params: Any @attrs.define class JsonRPCResponseMessage: """A class that represents a generic json rpc response message. Used as a fallback for unknown types. """ id: Union[int, str] jsonrpc: str result: Any class JsonRPCProtocol(asyncio.Protocol): """Json RPC protocol implementation using on top of `asyncio.Protocol`. Specification of the protocol can be found here: https://www.jsonrpc.org/specification This class provides bidirectional communication which is needed for LSP. """ CHARSET = "utf-8" CONTENT_TYPE = "application/vscode-jsonrpc" MESSAGE_PATTERN = re.compile( rb"^(?:[^\r\n]+\r\n)*" + rb"Content-Length: (?P\d+)\r\n" + rb"(?:[^\r\n]+\r\n)*\r\n" + rb"(?P{.*)", re.DOTALL, ) VERSION = "2.0" def __init__(self, server: LanguageServer, converter): self._server = server self._converter = converter self._shutdown = False # Book keeping for in-flight requests self._request_futures: Dict[str, Future[Any]] = {} self._result_types: Dict[str, Any] = {} self.fm = FeatureManager(server, converter) self.transport: Optional[ Union[asyncio.WriteTransport, WebSocketTransportAdapter] ] = None self._message_buf: List[bytes] = [] self._send_only_body = False def __call__(self): return self def _execute_notification(self, handler, *params): """Executes notification message handler.""" if asyncio.iscoroutinefunction(handler): future = asyncio.ensure_future(handler(*params)) future.add_done_callback(self._execute_notification_callback) else: if is_thread_function(handler): self._server.thread_pool.apply_async(handler, (*params,)) else: handler(*params) def _execute_notification_callback(self, future): """Success callback used for coroutine notification message.""" if future.exception(): try: raise future.exception() except Exception: error = JsonRpcInternalError.of(sys.exc_info()) logger.exception('Exception occurred in notification: "%s"', error) # Revisit. Client does not support response with msg_id = None # https://stackoverflow.com/questions/31091376/json-rpc-2-0-allow-notifications-to-have-an-error-response # self._send_response(None, error=error) def _execute_request(self, msg_id, handler, params): """Executes request message handler.""" if asyncio.iscoroutinefunction(handler): future = asyncio.ensure_future(handler(params)) self._request_futures[msg_id] = future future.add_done_callback(partial(self._execute_request_callback, msg_id)) else: # Can't be canceled if is_thread_function(handler): self._server.thread_pool.apply_async( handler, (params,), callback=partial( self._send_response, msg_id, ), error_callback=partial(self._execute_request_err_callback, msg_id), ) else: self._send_response(msg_id, handler(params)) def _execute_request_callback(self, msg_id, future): """Success callback used for coroutine request message.""" try: if not future.cancelled(): self._send_response(msg_id, result=future.result()) else: self._send_response( msg_id, error=JsonRpcRequestCancelled( f'Request with id "{msg_id}" is canceled' ).to_response_error(), ) self._request_futures.pop(msg_id, None) except Exception: error = JsonRpcInternalError.of(sys.exc_info()) logger.exception('Exception occurred for message "%s": %s', msg_id, error) self._send_response(msg_id, error=error.to_response_error()) def _execute_request_err_callback(self, msg_id, exc): """Error callback used for coroutine request message.""" exc_info = (type(exc), exc, None) error = JsonRpcInternalError.of(exc_info) logger.exception('Exception occurred for message "%s": %s', msg_id, error) self._send_response(msg_id, error=error.to_response_error()) def _get_handler(self, feature_name): """Returns builtin or used defined feature by name if exists.""" try: return self.fm.builtin_features[feature_name] except KeyError: try: return self.fm.features[feature_name] except KeyError: raise JsonRpcMethodNotFound.of(feature_name) def _handle_cancel_notification(self, msg_id): """Handles a cancel notification from the client.""" future = self._request_futures.pop(msg_id, None) if not future: logger.warning('Cancel notification for unknown message id "%s"', msg_id) return # Will only work if the request hasn't started executing if future.cancel(): logger.info('Cancelled request with id "%s"', msg_id) def _handle_notification(self, method_name, params): """Handles a notification from the client.""" if method_name == CANCEL_REQUEST: self._handle_cancel_notification(params.id) return try: handler = self._get_handler(method_name) self._execute_notification(handler, params) except (KeyError, JsonRpcMethodNotFound): logger.warning('Ignoring notification for unknown method "%s"', method_name) except Exception as error: logger.exception( 'Failed to handle notification "%s": %s', method_name, params, exc_info=True, ) self._server._report_server_error(error, FeatureNotificationError) def _handle_request(self, msg_id, method_name, params): """Handles a request from the client.""" try: handler = self._get_handler(method_name) # workspace/executeCommand is a special case if method_name == WORKSPACE_EXECUTE_COMMAND: handler(params, msg_id) else: self._execute_request(msg_id, handler, params) except JsonRpcException as error: logger.exception( "Failed to handle request %s %s %s", msg_id, method_name, params, exc_info=True, ) self._send_response(msg_id, None, error.to_response_error()) self._server._report_server_error(error, FeatureRequestError) except Exception as error: logger.exception( "Failed to handle request %s %s %s", msg_id, method_name, params, exc_info=True, ) err = JsonRpcInternalError.of(sys.exc_info()).to_response_error() self._send_response(msg_id, None, err) self._server._report_server_error(error, FeatureRequestError) def _handle_response(self, msg_id, result=None, error=None): """Handles a response from the client.""" future = self._request_futures.pop(msg_id, None) if not future: logger.warning('Received response to unknown message id "%s"', msg_id) return if error is not None: logger.debug('Received error response to message "%s": %s', msg_id, error) future.set_exception(JsonRpcException.from_error(error)) else: logger.debug('Received result for message "%s": %s', msg_id, result) future.set_result(result) def _serialize_message(self, data): """Function used to serialize data sent to the client.""" if hasattr(data, "__attrs_attrs__"): return self._converter.unstructure(data) if isinstance(data, enum.Enum): return data.value return data.__dict__ def _deserialize_message(self, data): """Function used to deserialize data recevied from the client.""" if "jsonrpc" not in data: return data try: if "id" in data: if "error" in data: return self._converter.structure(data, ResponseErrorMessage) elif "method" in data: request_type = ( self.get_message_type(data["method"]) or JsonRPCRequestMessage ) return self._converter.structure(data, request_type) else: response_type = ( self._result_types.pop(data["id"]) or JsonRPCResponseMessage ) return self._converter.structure(data, response_type) else: method = data.get("method", "") notification_type = self.get_message_type(method) or JsonRPCNotification return self._converter.structure(data, notification_type) except ClassValidationError as exc: logger.error("Unable to deserialize message\n%s", traceback.format_exc()) raise JsonRpcInvalidParams() from exc except Exception as exc: logger.error("Unable to deserialize message\n%s", traceback.format_exc()) raise JsonRpcInternalError() from exc def _procedure_handler(self, message): """Delegates message to handlers depending on message type.""" if message.jsonrpc != JsonRPCProtocol.VERSION: logger.warning('Unknown message "%s"', message) return if self._shutdown and getattr(message, "method", "") != EXIT: logger.warning("Server shutting down. No more requests!") return if hasattr(message, "method"): if hasattr(message, "id"): logger.debug("Request message received.") self._handle_request(message.id, message.method, message.params) else: logger.debug("Notification message received.") self._handle_notification(message.method, message.params) else: if hasattr(message, "error"): logger.debug("Error message received.") self._handle_response(message.id, None, message.error) else: logger.debug("Response message received.") self._handle_response(message.id, message.result) def _send_data(self, data): """Sends data to the client.""" if not data: return if self.transport is None: logger.error("Unable to send data, no available transport!") return try: body = json.dumps(data, default=self._serialize_message) logger.info("Sending data: %s", body) if self._send_only_body: # Mypy/Pyright seem to think `write()` wants `"bytes | bytearray | memoryview"` # But runtime errors with anything but `str`. self.transport.write(body) # type: ignore return header = ( f"Content-Length: {len(body)}\r\n" f"Content-Type: {self.CONTENT_TYPE}; charset={self.CHARSET}\r\n\r\n" ).encode(self.CHARSET) self.transport.write(header + body.encode(self.CHARSET)) except Exception as error: logger.exception("Error sending data", exc_info=True) self._server._report_server_error(error, JsonRpcInternalError) def _send_response( self, msg_id, result=None, error: Union[ResponseError, None] = None ): """Sends a JSON RPC response to the client. Args: msg_id(str): Id from request result(any): Result returned by handler error(any): Error returned by handler """ if error is not None: response = ResponseErrorMessage(id=msg_id, error=error) else: response_type = self._result_types.pop(msg_id, JsonRPCResponseMessage) response = response_type( id=msg_id, result=result, jsonrpc=JsonRPCProtocol.VERSION ) self._send_data(response) def connection_lost(self, exc): """Method from base class, called when connection is lost, in which case we want to shutdown the server's process as well. """ logger.error("Connection to the client is lost! Shutting down the server.") sys.exit(1) def connection_made( # type: ignore # see: https://github.com/python/typeshed/issues/3021 self, transport: asyncio.Transport, ): """Method from base class, called when connection is established""" self.transport = transport def data_received(self, data: bytes): try: self._data_received(data) except Exception as error: logger.exception("Error receiving data", exc_info=True) self._server._report_server_error(error, JsonRpcInternalError) def _data_received(self, data: bytes): """Method from base class, called when server receives the data""" logger.debug("Received %r", data) while len(data): # Append the incoming chunk to the message buffer self._message_buf.append(data) # Look for the body of the message message = b"".join(self._message_buf) found = JsonRPCProtocol.MESSAGE_PATTERN.fullmatch(message) body = found.group("body") if found else b"" length = int(found.group("length")) if found else 1 if len(body) < length: # Message is incomplete; bail until more data arrives return # Message is complete; # extract the body and any remaining data, # and reset the buffer for the next message body, data = body[:length], body[length:] self._message_buf = [] # Parse the body self._procedure_handler( json.loads( body.decode(self.CHARSET), object_hook=self._deserialize_message ) ) def get_message_type(self, method: str) -> Optional[Type]: """Return the type definition of the message associated with the given method.""" return None def get_result_type(self, method: str) -> Optional[Type]: """Return the type definition of the result associated with the given method.""" return None def notify(self, method: str, params=None): """Sends a JSON RPC notification to the client.""" logger.debug("Sending notification: '%s' %s", method, params) notification_type = self.get_message_type(method) or JsonRPCNotification notification = notification_type( method=method, params=params, jsonrpc=JsonRPCProtocol.VERSION ) self._send_data(notification) def send_request(self, method, params=None, callback=None, msg_id=None): """Sends a JSON RPC request to the client. Args: method(str): The method name of the message to send params(any): The payload of the message Returns: Future that will be resolved once a response has been received """ if msg_id is None: msg_id = str(uuid.uuid4()) request_type = self.get_message_type(method) or JsonRPCRequestMessage logger.debug('Sending request with id "%s": %s %s', msg_id, method, params) request = request_type( id=msg_id, method=method, params=params, jsonrpc=JsonRPCProtocol.VERSION, ) future = Future() # type: ignore[var-annotated] # If callback function is given, call it when result is received if callback: def wrapper(future: Future): result = future.result() logger.info("Client response for %s received: %s", params, result) callback(result) future.add_done_callback(wrapper) self._request_futures[msg_id] = future self._result_types[msg_id] = self.get_result_type(method) self._send_data(request) return future def send_request_async(self, method, params=None, msg_id=None): """Calls `send_request` and wraps `concurrent.futures.Future` with `asyncio.Future` so it can be used with `await` keyword. Args: method(str): The method name of the message to send params(any): The payload of the message msg_id(str|int): Optional, message id Returns: `asyncio.Future` that can be awaited """ return asyncio.wrap_future( self.send_request(method, params=params, msg_id=msg_id) ) def thread(self): """Decorator that mark function to execute it in a thread.""" return self.fm.thread() pygls-1.3.0/pygls/protocol/language_server.py000066400000000000000000000472151455570166200214170ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from __future__ import annotations import asyncio import json import logging import sys from concurrent.futures import Future from functools import lru_cache from itertools import zip_longest from typing import ( Callable, List, Optional, Type, TypeVar, Union, ) from pygls.capabilities import ServerCapabilitiesBuilder from pygls.lsp import ConfigCallbackType, ShowDocumentCallbackType from lsprotocol.types import ( CLIENT_REGISTER_CAPABILITY, CLIENT_UNREGISTER_CAPABILITY, EXIT, INITIALIZE, INITIALIZED, METHOD_TO_TYPES, NOTEBOOK_DOCUMENT_DID_CHANGE, NOTEBOOK_DOCUMENT_DID_CLOSE, NOTEBOOK_DOCUMENT_DID_OPEN, LOG_TRACE, SET_TRACE, SHUTDOWN, TEXT_DOCUMENT_DID_CHANGE, TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, WINDOW_LOG_MESSAGE, WINDOW_SHOW_DOCUMENT, WINDOW_SHOW_MESSAGE, WINDOW_WORK_DONE_PROGRESS_CANCEL, WORKSPACE_APPLY_EDIT, WORKSPACE_CONFIGURATION, WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS, WORKSPACE_EXECUTE_COMMAND, WORKSPACE_SEMANTIC_TOKENS_REFRESH, ) from lsprotocol.types import ( ApplyWorkspaceEditParams, Diagnostic, DidChangeNotebookDocumentParams, DidChangeTextDocumentParams, DidChangeWorkspaceFoldersParams, DidCloseNotebookDocumentParams, DidCloseTextDocumentParams, DidOpenNotebookDocumentParams, DidOpenTextDocumentParams, ExecuteCommandParams, InitializeParams, InitializeResult, LogMessageParams, LogTraceParams, MessageType, PublishDiagnosticsParams, RegistrationParams, SetTraceParams, ShowDocumentParams, ShowMessageParams, TraceValues, UnregistrationParams, WorkspaceApplyEditResponse, WorkspaceEdit, InitializeResultServerInfoType, WorkspaceConfigurationParams, WorkDoneProgressCancelParams, ) from pygls.protocol.json_rpc import JsonRPCProtocol from pygls.protocol.lsp_meta import LSPMeta from pygls.uris import from_fs_path from pygls.workspace import Workspace F = TypeVar("F", bound=Callable) logger = logging.getLogger(__name__) def lsp_method(method_name: str) -> Callable[[F], F]: def decorator(f: F) -> F: f.method_name = method_name # type: ignore[attr-defined] return f return decorator class LanguageServerProtocol(JsonRPCProtocol, metaclass=LSPMeta): """A class that represents language server protocol. It contains implementations for generic LSP features. Attributes: workspace(Workspace): In memory workspace """ def __init__(self, server, converter): super().__init__(server, converter) self._workspace: Optional[Workspace] = None self.trace = None from pygls.progress import Progress self.progress = Progress(self) self.server_info = InitializeResultServerInfoType( name=server.name, version=server.version, ) self._register_builtin_features() def _register_builtin_features(self): """Registers generic LSP features from this class.""" for name in dir(self): if name in {"workspace"}: continue attr = getattr(self, name) if callable(attr) and hasattr(attr, "method_name"): self.fm.add_builtin_feature(attr.method_name, attr) @property def workspace(self) -> Workspace: if self._workspace is None: raise RuntimeError( "The workspace is not available - has the server been initialized?" ) return self._workspace @lru_cache() def get_message_type(self, method: str) -> Optional[Type]: """Return LSP type definitions, as provided by `lsprotocol`""" return METHOD_TO_TYPES.get(method, (None,))[0] @lru_cache() def get_result_type(self, method: str) -> Optional[Type]: return METHOD_TO_TYPES.get(method, (None, None))[1] def apply_edit( self, edit: WorkspaceEdit, label: Optional[str] = None ) -> WorkspaceApplyEditResponse: """Sends apply edit request to the client.""" return self.send_request( WORKSPACE_APPLY_EDIT, ApplyWorkspaceEditParams(edit=edit, label=label) ) def apply_edit_async( self, edit: WorkspaceEdit, label: Optional[str] = None ) -> WorkspaceApplyEditResponse: """Sends apply edit request to the client. Should be called with `await`""" return self.send_request_async( WORKSPACE_APPLY_EDIT, ApplyWorkspaceEditParams(edit=edit, label=label) ) @lsp_method(EXIT) def lsp_exit(self, *args) -> None: """Stops the server process.""" if self.transport is not None: self.transport.close() sys.exit(0 if self._shutdown else 1) @lsp_method(INITIALIZE) def lsp_initialize(self, params: InitializeParams) -> InitializeResult: """Method that initializes language server. It will compute and return server capabilities based on registered features. """ logger.info("Language server initialized %s", params) self._server.process_id = params.process_id text_document_sync_kind = self._server._text_document_sync_kind notebook_document_sync = self._server._notebook_document_sync # Initialize server capabilities self.client_capabilities = params.capabilities self.server_capabilities = ServerCapabilitiesBuilder( self.client_capabilities, set({**self.fm.features, **self.fm.builtin_features}.keys()), self.fm.feature_options, list(self.fm.commands.keys()), text_document_sync_kind, notebook_document_sync, ).build() logger.debug( "Server capabilities: %s", json.dumps(self.server_capabilities, default=self._serialize_message), ) root_path = params.root_path root_uri = params.root_uri if root_path is not None and root_uri is None: root_uri = from_fs_path(root_path) # Initialize the workspace workspace_folders = params.workspace_folders or [] self._workspace = Workspace( root_uri, text_document_sync_kind, workspace_folders, self.server_capabilities.position_encoding, ) self.trace = TraceValues.Off return InitializeResult( capabilities=self.server_capabilities, server_info=self.server_info, ) @lsp_method(INITIALIZED) def lsp_initialized(self, *args) -> None: """Notification received when client and server are connected.""" pass @lsp_method(SHUTDOWN) def lsp_shutdown(self, *args) -> None: """Request from client which asks server to shutdown.""" for future in self._request_futures.values(): future.cancel() self._shutdown = True return None @lsp_method(TEXT_DOCUMENT_DID_CHANGE) def lsp_text_document__did_change( self, params: DidChangeTextDocumentParams ) -> None: """Updates document's content. (Incremental(from server capabilities); not configurable for now) """ for change in params.content_changes: self.workspace.update_text_document(params.text_document, change) @lsp_method(TEXT_DOCUMENT_DID_CLOSE) def lsp_text_document__did_close(self, params: DidCloseTextDocumentParams) -> None: """Removes document from workspace.""" self.workspace.remove_text_document(params.text_document.uri) @lsp_method(TEXT_DOCUMENT_DID_OPEN) def lsp_text_document__did_open(self, params: DidOpenTextDocumentParams) -> None: """Puts document to the workspace.""" self.workspace.put_text_document(params.text_document) @lsp_method(NOTEBOOK_DOCUMENT_DID_OPEN) def lsp_notebook_document__did_open( self, params: DidOpenNotebookDocumentParams ) -> None: """Put a notebook document into the workspace""" self.workspace.put_notebook_document(params) @lsp_method(NOTEBOOK_DOCUMENT_DID_CHANGE) def lsp_notebook_document__did_change( self, params: DidChangeNotebookDocumentParams ) -> None: """Update a notebook's contents""" self.workspace.update_notebook_document(params) @lsp_method(NOTEBOOK_DOCUMENT_DID_CLOSE) def lsp_notebook_document__did_close( self, params: DidCloseNotebookDocumentParams ) -> None: """Remove a notebook document from the workspace.""" self.workspace.remove_notebook_document(params) @lsp_method(SET_TRACE) def lsp_set_trace(self, params: SetTraceParams) -> None: """Changes server trace value.""" self.trace = params.value @lsp_method(WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS) def lsp_workspace__did_change_workspace_folders( self, params: DidChangeWorkspaceFoldersParams ) -> None: """Adds/Removes folders from the workspace.""" logger.info("Workspace folders changed: %s", params) added_folders = params.event.added or [] removed_folders = params.event.removed or [] for f_add, f_remove in zip_longest(added_folders, removed_folders): if f_add: self.workspace.add_folder(f_add) if f_remove: self.workspace.remove_folder(f_remove.uri) @lsp_method(WORKSPACE_EXECUTE_COMMAND) def lsp_workspace__execute_command( self, params: ExecuteCommandParams, msg_id: str ) -> None: """Executes commands with passed arguments and returns a value.""" cmd_handler = self.fm.commands[params.command] self._execute_request(msg_id, cmd_handler, params.arguments) @lsp_method(WINDOW_WORK_DONE_PROGRESS_CANCEL) def lsp_work_done_progress_cancel( self, params: WorkDoneProgressCancelParams ) -> None: """Received a progress cancellation from client.""" future = self.progress.tokens.get(params.token) if future is None: logger.warning( "Ignoring work done progress cancel for unknown token %s", params.token ) else: future.cancel() def get_configuration( self, params: WorkspaceConfigurationParams, callback: Optional[ConfigCallbackType] = None, ) -> Future: """Sends configuration request to the client. Args: params(WorkspaceConfigurationParams): WorkspaceConfigurationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(WORKSPACE_CONFIGURATION, params, callback) def get_configuration_async( self, params: WorkspaceConfigurationParams ) -> asyncio.Future: """Calls `get_configuration` method but designed to use with coroutines Args: params(WorkspaceConfigurationParams): WorkspaceConfigurationParams from lsp specs Returns: asyncio.Future that can be awaited """ return asyncio.wrap_future(self.get_configuration(params)) def log_trace(self, message: str, verbose: Optional[str] = None) -> None: """Sends trace notification to the client.""" if self.trace == TraceValues.Off: return params = LogTraceParams(message=message) if verbose and self.trace == TraceValues.Verbose: params.verbose = verbose self.notify(LOG_TRACE, params) def _publish_diagnostics_deprecator( self, params_or_uri: Union[str, PublishDiagnosticsParams], diagnostics: Optional[List[Diagnostic]], version: Optional[int], **kwargs, ) -> PublishDiagnosticsParams: if isinstance(params_or_uri, str): message = "DEPRECATION: " "`publish_diagnostics(" "self, doc_uri: str, diagnostics: List[Diagnostic], version: Optional[int] = None)`" "will be replaced with `publish_diagnostics(self, params: PublishDiagnosticsParams)`" logging.warning(message) params = self._construct_publish_diagnostic_type( params_or_uri, diagnostics, version, **kwargs ) else: params = params_or_uri return params def _construct_publish_diagnostic_type( self, uri: str, diagnostics: Optional[List[Diagnostic]], version: Optional[int], **kwargs, ) -> PublishDiagnosticsParams: if diagnostics is None: diagnostics = [] args = { **{"uri": uri, "diagnostics": diagnostics, "version": version}, **kwargs, } params = PublishDiagnosticsParams(**args) # type:ignore return params def publish_diagnostics( self, params_or_uri: Union[str, PublishDiagnosticsParams], diagnostics: Optional[List[Diagnostic]] = None, version: Optional[int] = None, **kwargs, ): """Sends diagnostic notification to the client. .. deprecated:: 1.0.1 Passing ``(uri, diagnostics, version)`` as arguments is deprecated. Pass an instance of :class:`~lsprotocol.types.PublishDiagnosticParams` instead. Parameters ---------- params_or_uri The :class:`~lsprotocol.types.PublishDiagnosticParams` to send to the client. diagnostics *Deprecated*. The diagnostics to publish version *Deprecated*: The version number """ params = self._publish_diagnostics_deprecator( params_or_uri, diagnostics, version, **kwargs ) self.notify(TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, params) def register_capability( self, params: RegistrationParams, callback: Optional[Callable[[], None]] = None ) -> Future: """Register a new capability on the client. Args: params(RegistrationParams): RegistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(CLIENT_REGISTER_CAPABILITY, params, callback) def register_capability_async(self, params: RegistrationParams) -> asyncio.Future: """Register a new capability on the client. Args: params(RegistrationParams): RegistrationParams from lsp specs Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.register_capability(params, None)) def semantic_tokens_refresh( self, callback: Optional[Callable[[], None]] = None ) -> Future: """Requesting a refresh of all semantic tokens. Args: callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(WORKSPACE_SEMANTIC_TOKENS_REFRESH, callback=callback) def semantic_tokens_refresh_async(self) -> asyncio.Future: """Requesting a refresh of all semantic tokens. Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.semantic_tokens_refresh(None)) def show_document( self, params: ShowDocumentParams, callback: Optional[ShowDocumentCallbackType] = None, ) -> Future: """Display a particular document in the user interface. Args: params(ShowDocumentParams): ShowDocumentParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(WINDOW_SHOW_DOCUMENT, params, callback) def show_document_async(self, params: ShowDocumentParams) -> asyncio.Future: """Display a particular document in the user interface. Args: params(ShowDocumentParams): ShowDocumentParams from lsp specs Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.show_document(params, None)) def show_message(self, message, msg_type=MessageType.Info): """Sends message to the client to display message.""" self.notify( WINDOW_SHOW_MESSAGE, ShowMessageParams(type=msg_type, message=message) ) def show_message_log(self, message, msg_type=MessageType.Log): """Sends message to the client's output channel.""" self.notify( WINDOW_LOG_MESSAGE, LogMessageParams(type=msg_type, message=message) ) def unregister_capability( self, params: UnregistrationParams, callback: Optional[Callable[[], None]] = None, ) -> Future: """Unregister a new capability on the client. Args: params(UnregistrationParams): UnregistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(CLIENT_UNREGISTER_CAPABILITY, params, callback) def unregister_capability_async( self, params: UnregistrationParams ) -> asyncio.Future: """Unregister a new capability on the client. Args: params(UnregistrationParams): UnregistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.unregister_capability(params, None)) pygls-1.3.0/pygls/protocol/lsp_meta.py000066400000000000000000000030711455570166200200420ustar00rootroot00000000000000import functools import logging from pygls.constants import ATTR_FEATURE_TYPE from pygls.feature_manager import assign_help_attrs logger = logging.getLogger(__name__) def call_user_feature(base_func, method_name): """Wraps generic LSP features and calls user registered feature immediately after it. """ @functools.wraps(base_func) def decorator(self, *args, **kwargs): ret_val = base_func(self, *args, **kwargs) try: user_func = self.fm.features[method_name] self._execute_notification(user_func, *args, **kwargs) except KeyError: pass except Exception: logger.exception( 'Failed to handle user defined notification "%s": %s', method_name, args ) return ret_val return decorator class LSPMeta(type): """Wraps LSP built-in features (`lsp_` naming convention). Built-in features cannot be overridden but user defined features with the same LSP name will be called after them. """ def __new__(mcs, cls_name, cls_bases, cls): for attr_name, attr_val in cls.items(): if callable(attr_val) and hasattr(attr_val, "method_name"): method_name = attr_val.method_name wrapped = call_user_feature(attr_val, method_name) assign_help_attrs(wrapped, method_name, ATTR_FEATURE_TYPE) cls[attr_name] = wrapped logger.debug('Added decorator for lsp method: "%s"', attr_name) return super().__new__(mcs, cls_name, cls_bases, cls) pygls-1.3.0/pygls/py.typed000066400000000000000000000001011455570166200155110ustar00rootroot00000000000000# Marker file for PEP 561. The pygls package uses inline types. pygls-1.3.0/pygls/server.py000066400000000000000000000504211455570166200157040ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio import json import logging import re import sys from concurrent.futures import Future, ThreadPoolExecutor from threading import Event from typing import ( Any, Callable, List, Optional, TextIO, Type, TypeVar, Union, ) import cattrs from pygls import IS_PYODIDE from pygls.lsp import ConfigCallbackType, ShowDocumentCallbackType from pygls.exceptions import ( FeatureNotificationError, JsonRpcInternalError, PyglsError, JsonRpcException, FeatureRequestError, ) from lsprotocol.types import ( ClientCapabilities, Diagnostic, MessageType, NotebookDocumentSyncOptions, RegistrationParams, ServerCapabilities, ShowDocumentParams, TextDocumentSyncKind, UnregistrationParams, WorkspaceApplyEditResponse, WorkspaceEdit, WorkspaceConfigurationParams, ) from pygls.progress import Progress from pygls.protocol import JsonRPCProtocol, LanguageServerProtocol, default_converter from pygls.workspace import Workspace if not IS_PYODIDE: from multiprocessing.pool import ThreadPool logger = logging.getLogger(__name__) F = TypeVar("F", bound=Callable) ServerErrors = Union[ PyglsError, JsonRpcException, Type[JsonRpcInternalError], Type[FeatureNotificationError], Type[FeatureRequestError], ] async def aio_readline(loop, executor, stop_event, rfile, proxy): """Reads data from stdin in separate thread (asynchronously).""" CONTENT_LENGTH_PATTERN = re.compile(rb"^Content-Length: (\d+)\r\n$") # Initialize message buffer message = [] content_length = 0 while not stop_event.is_set() and not rfile.closed: # Read a header line header = await loop.run_in_executor(executor, rfile.readline) if not header: break message.append(header) # Extract content length if possible if not content_length: match = CONTENT_LENGTH_PATTERN.fullmatch(header) if match: content_length = int(match.group(1)) logger.debug("Content length: %s", content_length) # Check if all headers have been read (as indicated by an empty line \r\n) if content_length and not header.strip(): # Read body body = await loop.run_in_executor(executor, rfile.read, content_length) if not body: break message.append(body) # Pass message to language server protocol proxy(b"".join(message)) # Reset the buffer message = [] content_length = 0 class StdOutTransportAdapter: """Protocol adapter which overrides write method. Write method sends data to stdout. """ def __init__(self, rfile, wfile): self.rfile = rfile self.wfile = wfile def close(self): self.rfile.close() self.wfile.close() def write(self, data): self.wfile.write(data) self.wfile.flush() class PyodideTransportAdapter: """Protocol adapter which overrides write method. Write method sends data to stdout. """ def __init__(self, wfile): self.wfile = wfile def close(self): self.wfile.close() def write(self, data): self.wfile.write(data) self.wfile.flush() class WebSocketTransportAdapter: """Protocol adapter which calls write method. Write method sends data via the WebSocket interface. """ def __init__(self, ws, loop): self._ws = ws self._loop = loop def close(self) -> None: """Stop the WebSocket server.""" self._ws.close() def write(self, data: Any) -> None: """Create a task to write specified data into a WebSocket.""" asyncio.ensure_future(self._ws.send(data)) class Server: """Base server class Parameters ---------- protocol_cls Protocol implementation that must be derive from :class:`~pygls.protocol.JsonRPCProtocol` converter_factory Factory function to use when constructing a cattrs converter. loop The asyncio event loop max_workers Maximum number of workers for `ThreadPool` and `ThreadPoolExecutor` """ def __init__( self, protocol_cls: Type[JsonRPCProtocol], converter_factory: Callable[[], cattrs.Converter], loop: Optional[asyncio.AbstractEventLoop] = None, max_workers: int = 2, sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental, ): if not issubclass(protocol_cls, asyncio.Protocol): raise TypeError("Protocol class should be subclass of asyncio.Protocol") self._max_workers = max_workers self._server = None self._stop_event: Optional[Event] = None self._thread_pool: Optional[ThreadPool] = None self._thread_pool_executor: Optional[ThreadPoolExecutor] = None if sync_kind is not None: self.text_document_sync_kind = sync_kind if loop is None: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) self._owns_loop = True else: self._owns_loop = False self.loop = loop # TODO: Will move this to `LanguageServer` soon self.lsp = protocol_cls(self, converter_factory()) # type: ignore def shutdown(self): """Shutdown server.""" logger.info("Shutting down the server") if self._stop_event is not None: self._stop_event.set() if self._thread_pool: self._thread_pool.terminate() self._thread_pool.join() if self._thread_pool_executor: self._thread_pool_executor.shutdown() if self._server: self._server.close() self.loop.run_until_complete(self._server.wait_closed()) if self._owns_loop and not self.loop.is_closed(): logger.info("Closing the event loop.") self.loop.close() def start_io(self, stdin: Optional[TextIO] = None, stdout: Optional[TextIO] = None): """Starts IO server.""" logger.info("Starting IO server") self._stop_event = Event() transport = StdOutTransportAdapter( stdin or sys.stdin.buffer, stdout or sys.stdout.buffer ) self.lsp.connection_made(transport) # type: ignore[arg-type] try: self.loop.run_until_complete( aio_readline( self.loop, self.thread_pool_executor, self._stop_event, stdin or sys.stdin.buffer, self.lsp.data_received, ) ) except BrokenPipeError: logger.error("Connection to the client is lost! Shutting down the server.") except (KeyboardInterrupt, SystemExit): pass finally: self.shutdown() def start_pyodide(self): logger.info("Starting Pyodide server") # Note: We don't actually start anything running as the main event # loop will be handled by the web platform. transport = PyodideTransportAdapter(sys.stdout) self.lsp.connection_made(transport) # type: ignore[arg-type] self.lsp._send_only_body = True # Don't send headers within the payload def start_tcp(self, host: str, port: int) -> None: """Starts TCP server.""" logger.info("Starting TCP server on %s:%s", host, port) self._stop_event = Event() self._server = self.loop.run_until_complete( # type: ignore[assignment] self.loop.create_server(self.lsp, host, port) ) try: self.loop.run_forever() except (KeyboardInterrupt, SystemExit): pass finally: self.shutdown() def start_ws(self, host: str, port: int) -> None: """Starts WebSocket server.""" try: from websockets.server import serve except ImportError: logger.error("Run `pip install pygls[ws]` to install `websockets`.") sys.exit(1) logger.info("Starting WebSocket server on {}:{}".format(host, port)) self._stop_event = Event() self.lsp._send_only_body = True # Don't send headers within the payload async def connection_made(websocket, _): """Handle new connection wrapped in the WebSocket.""" self.lsp.transport = WebSocketTransportAdapter(websocket, self.loop) async for message in websocket: self.lsp._procedure_handler( json.loads(message, object_hook=self.lsp._deserialize_message) ) start_server = serve(connection_made, host, port, loop=self.loop) self._server = start_server.ws_server # type: ignore[assignment] self.loop.run_until_complete(start_server) try: self.loop.run_forever() except (KeyboardInterrupt, SystemExit): pass finally: self._stop_event.set() self.shutdown() if not IS_PYODIDE: @property def thread_pool(self) -> ThreadPool: """Returns thread pool instance (lazy initialization).""" if not self._thread_pool: self._thread_pool = ThreadPool(processes=self._max_workers) return self._thread_pool @property def thread_pool_executor(self) -> ThreadPoolExecutor: """Returns thread pool instance (lazy initialization).""" if not self._thread_pool_executor: self._thread_pool_executor = ThreadPoolExecutor( max_workers=self._max_workers ) return self._thread_pool_executor class LanguageServer(Server): """The default LanguageServer This class can be extended and it can be passed as a first argument to registered commands/features. .. |ServerInfo| replace:: :class:`~lsprotocol.types.InitializeResultServerInfoType` Parameters ---------- name Name of the server, used to populate |ServerInfo| which is sent to the client during initialization version Version of the server, used to populate |ServerInfo| which is sent to the client during initialization protocol_cls The :class:`~pygls.protocol.LanguageServerProtocol` class definition, or any subclass of it. max_workers Maximum number of workers for ``ThreadPool`` and ``ThreadPoolExecutor`` text_document_sync_kind Text document synchronization method None No synchronization :attr:`~lsprotocol.types.TextDocumentSyncKind.Full` Send entire document text with each update :attr:`~lsprotocol.types.TextDocumentSyncKind.Incremental` Send only the region of text that changed with each update notebook_document_sync Advertise :lsp:`NotebookDocument` support to the client. """ lsp: LanguageServerProtocol default_error_message = ( "Unexpected error in LSP server, see server's logs for details" ) """ The default error message sent to the user's editor when this server encounters an uncaught exception. """ def __init__( self, name: str, version: str, loop=None, protocol_cls: Type[LanguageServerProtocol] = LanguageServerProtocol, converter_factory=default_converter, text_document_sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental, notebook_document_sync: Optional[NotebookDocumentSyncOptions] = None, max_workers: int = 2, ): if not issubclass(protocol_cls, LanguageServerProtocol): raise TypeError( "Protocol class should be subclass of LanguageServerProtocol" ) self.name = name self.version = version self._text_document_sync_kind = text_document_sync_kind self._notebook_document_sync = notebook_document_sync self.process_id: Optional[Union[int, None]] = None super().__init__(protocol_cls, converter_factory, loop, max_workers) def apply_edit( self, edit: WorkspaceEdit, label: Optional[str] = None ) -> WorkspaceApplyEditResponse: """Sends apply edit request to the client.""" return self.lsp.apply_edit(edit, label) def apply_edit_async( self, edit: WorkspaceEdit, label: Optional[str] = None ) -> WorkspaceApplyEditResponse: """Sends apply edit request to the client. Should be called with `await`""" return self.lsp.apply_edit_async(edit, label) def command(self, command_name: str) -> Callable[[F], F]: """Decorator used to register custom commands. Example ------- :: @ls.command('myCustomCommand') def my_cmd(ls, a, b, c): pass """ return self.lsp.fm.command(command_name) @property def client_capabilities(self) -> ClientCapabilities: """The client's capabilities.""" return self.lsp.client_capabilities def feature( self, feature_name: str, options: Optional[Any] = None, ) -> Callable[[F], F]: """Decorator used to register LSP features. Example ------- :: @ls.feature('textDocument/completion', CompletionOptions(trigger_characters=['.'])) def completions(ls, params: CompletionParams): return CompletionList(is_incomplete=False, items=[CompletionItem("Completion 1")]) """ return self.lsp.fm.feature(feature_name, options) def get_configuration( self, params: WorkspaceConfigurationParams, callback: Optional[ConfigCallbackType] = None, ) -> Future: """Gets the configuration settings from the client.""" return self.lsp.get_configuration(params, callback) def get_configuration_async( self, params: WorkspaceConfigurationParams ) -> asyncio.Future: """Gets the configuration settings from the client. Should be called with `await`""" return self.lsp.get_configuration_async(params) def log_trace(self, message: str, verbose: Optional[str] = None) -> None: """Sends trace notification to the client.""" self.lsp.log_trace(message, verbose) @property def progress(self) -> Progress: """Gets the object to manage client's progress bar.""" return self.lsp.progress def publish_diagnostics( self, uri: str, diagnostics: Optional[List[Diagnostic]] = None, version: Optional[int] = None, **kwargs ): """ Sends diagnostic notification to the client. """ params = self.lsp._construct_publish_diagnostic_type( uri, diagnostics, version, **kwargs ) self.lsp.publish_diagnostics(params, **kwargs) def register_capability( self, params: RegistrationParams, callback: Optional[Callable[[], None]] = None ) -> Future: """Register a new capability on the client.""" return self.lsp.register_capability(params, callback) def register_capability_async(self, params: RegistrationParams) -> asyncio.Future: """Register a new capability on the client. Should be called with `await`""" return self.lsp.register_capability_async(params) def semantic_tokens_refresh( self, callback: Optional[Callable[[], None]] = None ) -> Future: """Request a refresh of all semantic tokens.""" return self.lsp.semantic_tokens_refresh(callback) def semantic_tokens_refresh_async(self) -> asyncio.Future: """Request a refresh of all semantic tokens. Should be called with `await`""" return self.lsp.semantic_tokens_refresh_async() def send_notification(self, method: str, params: object = None) -> None: """Sends notification to the client.""" self.lsp.notify(method, params) @property def server_capabilities(self) -> ServerCapabilities: """Return server capabilities.""" return self.lsp.server_capabilities def show_document( self, params: ShowDocumentParams, callback: Optional[ShowDocumentCallbackType] = None, ) -> Future: """Display a particular document in the user interface.""" return self.lsp.show_document(params, callback) def show_document_async(self, params: ShowDocumentParams) -> asyncio.Future: """Display a particular document in the user interface. Should be called with `await`""" return self.lsp.show_document_async(params) def show_message(self, message, msg_type=MessageType.Info) -> None: """Sends message to the client to display message.""" self.lsp.show_message(message, msg_type) def show_message_log(self, message, msg_type=MessageType.Log) -> None: """Sends message to the client's output channel.""" self.lsp.show_message_log(message, msg_type) def _report_server_error( self, error: Exception, source: ServerErrors, ): # Prevent recursive error reporting try: self.report_server_error(error, source) except Exception: logger.warning("Failed to report error to client") def report_server_error(self, error: Exception, source: ServerErrors): """ Sends error to the client for displaying. By default this fucntion does not handle LSP request errors. This is because LSP requests require direct responses and so already have a mechanism for including unexpected errors in the response body. All other errors are "out of band" in the sense that the client isn't explicitly waiting for them. For example diagnostics are returned as notifications, not responses to requests, and so can seemingly be sent at random. Also for example consider JSON RPC serialization and deserialization, if a payload cannot be parsed then the whole request/response cycle cannot be completed and so one of these "out of band" error messages is sent. These "out of band" error messages are not a requirement of the LSP spec. Pygls simply offers this behaviour as a recommended default. It is perfectly reasonble to override this default. """ if source == FeatureRequestError: return self.show_message(self.default_error_message, msg_type=MessageType.Error) def thread(self) -> Callable[[F], F]: """Decorator that mark function to execute it in a thread.""" return self.lsp.thread() def unregister_capability( self, params: UnregistrationParams, callback: Optional[Callable[[], None]] = None, ) -> Future: """Unregister a new capability on the client.""" return self.lsp.unregister_capability(params, callback) def unregister_capability_async( self, params: UnregistrationParams ) -> asyncio.Future: """Unregister a new capability on the client. Should be called with `await`""" return self.lsp.unregister_capability_async(params) @property def workspace(self) -> Workspace: """Returns in-memory workspace.""" return self.lsp.workspace pygls-1.3.0/pygls/uris.py000066400000000000000000000132041455570166200153560ustar00rootroot00000000000000############################################################################ # Original work Copyright 2017 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ """ A collection of URI utilities with logic built on the VSCode URI library. https://github.com/Microsoft/vscode-uri/blob/e59cab84f5df6265aed18ae5f43552d3eef13bb9/lib/index.ts """ from typing import Optional, Tuple import re from urllib import parse from pygls import IS_WIN RE_DRIVE_LETTER_PATH = re.compile(r"^\/[a-zA-Z]:") URLParts = Tuple[str, str, str, str, str, str] def _normalize_win_path(path: str): netloc = "" # normalize to fwd-slashes on windows, # on other systems bwd-slashes are valid # filename character, eg /f\oo/ba\r.txt if IS_WIN: path = path.replace("\\", "/") # check for authority as used in UNC shares # or use the path as given if path[:2] == "//": idx = path.index("/", 2) if idx == -1: netloc = path[2:] else: netloc = path[2:idx] path = path[idx:] # Ensure that path starts with a slash # or that it is at least a slash if not path.startswith("/"): path = "/" + path # Normalize drive paths to lower case if RE_DRIVE_LETTER_PATH.match(path): path = path[0] + path[1].lower() + path[2:] return path, netloc def from_fs_path(path: str): """Returns a URI for the given filesystem path.""" try: scheme = "file" params, query, fragment = "", "", "" path, netloc = _normalize_win_path(path) return urlunparse((scheme, netloc, path, params, query, fragment)) except (AttributeError, TypeError): return None def to_fs_path(uri: str): """ Returns the filesystem path of the given URI. Will handle UNC paths and normalize windows drive letters to lower-case. Also uses the platform specific path separator. Will *not* validate the path for invalid characters and semantics. Will *not* look at the scheme of this URI. """ try: # scheme://netloc/path;parameters?query#fragment scheme, netloc, path, _, _, _ = urlparse(uri) if netloc and path and scheme == "file": # unc path: file://shares/c$/far/boo value = f"//{netloc}{path}" elif RE_DRIVE_LETTER_PATH.match(path): # windows drive letter: file:///C:/far/boo value = path[1].lower() + path[2:] else: # Other path value = path if IS_WIN: value = value.replace("/", "\\") return value except TypeError: return None def uri_scheme(uri: str): try: return urlparse(uri)[0] except (TypeError, IndexError): return None # TODO: Use `URLParts` type def uri_with( uri: str, scheme: Optional[str] = None, netloc: Optional[str] = None, path: Optional[str] = None, params: Optional[str] = None, query: Optional[str] = None, fragment: Optional[str] = None, ): """ Return a URI with the given part(s) replaced. Parts are decoded / encoded. """ old_scheme, old_netloc, old_path, old_params, old_query, old_fragment = urlparse( uri ) if path is None: raise Exception("`path` must not be None") path, _ = _normalize_win_path(path) return urlunparse( ( scheme or old_scheme, netloc or old_netloc, path or old_path, params or old_params, query or old_query, fragment or old_fragment, ) ) def urlparse(uri: str): """Parse and decode the parts of a URI.""" scheme, netloc, path, params, query, fragment = parse.urlparse(uri) return ( parse.unquote(scheme), parse.unquote(netloc), parse.unquote(path), parse.unquote(params), parse.unquote(query), parse.unquote(fragment), ) def urlunparse(parts: URLParts) -> str: """Unparse and encode parts of a URI.""" scheme, netloc, path, params, query, fragment = parts # Avoid encoding the windows drive letter colon if RE_DRIVE_LETTER_PATH.match(path): quoted_path = path[:3] + parse.quote(path[3:]) else: quoted_path = parse.quote(path) return parse.urlunparse( ( parse.quote(scheme), parse.quote(netloc), quoted_path, parse.quote(params), parse.quote(query), parse.quote(fragment), ) ) pygls-1.3.0/pygls/workspace/000077500000000000000000000000001455570166200160205ustar00rootroot00000000000000pygls-1.3.0/pygls/workspace/__init__.py000066400000000000000000000055031455570166200201340ustar00rootroot00000000000000from typing import List import warnings from lsprotocol import types from .workspace import Workspace from .text_document import TextDocument from .position_codec import PositionCodec # For backwards compatibility Document = TextDocument def utf16_unit_offset(chars: str): warnings.warn( "'utf16_unit_offset' has been deprecated, instead use " "'PositionCodec.utf16_unit_offset' via 'workspace.position_codec' " "or 'text_document.position_codec'", DeprecationWarning, stacklevel=2, ) _codec = PositionCodec() return _codec.utf16_unit_offset(chars) def utf16_num_units(chars: str): warnings.warn( "'utf16_num_units' has been deprecated, instead use " "'PositionCodec.client_num_units' via 'workspace.position_codec' " "or 'text_document.position_codec'", DeprecationWarning, stacklevel=2, ) _codec = PositionCodec() return _codec.client_num_units(chars) def position_from_utf16(lines: List[str], position: types.Position): warnings.warn( "'position_from_utf16' has been deprecated, instead use " "'PositionCodec.position_from_client_units' via " "'workspace.position_codec' or 'text_document.position_codec'", DeprecationWarning, stacklevel=2, ) _codec = PositionCodec() return _codec.position_from_client_units(lines, position) def position_to_utf16(lines: List[str], position: types.Position): warnings.warn( "'position_to_utf16' has been deprecated, instead use " "'PositionCodec.position_to_client_units' via " "'workspace.position_codec' or 'text_document.position_codec'", DeprecationWarning, stacklevel=2, ) _codec = PositionCodec() return _codec.position_to_client_units(lines, position) def range_from_utf16(lines: List[str], range: types.Range): warnings.warn( "'range_from_utf16' has been deprecated, instead use " "'PositionCodec.range_from_client_units' via " "'workspace.position_codec' or 'text_document.position_codec'", DeprecationWarning, stacklevel=2, ) _codec = PositionCodec() return _codec.range_from_client_units(lines, range) def range_to_utf16(lines: List[str], range: types.Range): warnings.warn( "'range_to_utf16' has been deprecated, instead use " "'PositionCodec.range_to_client_units' via 'workspace.position_codec' " "or 'text_document.position_codec'", DeprecationWarning, stacklevel=2, ) _codec = PositionCodec() return _codec.range_to_client_units(lines, range) __all__ = ( "Workspace", "TextDocument", "PositionCodec", "Document", "utf16_unit_offset", "utf16_num_units", "position_from_utf16", "position_to_utf16", "range_from_utf16", "range_to_utf16", ) pygls-1.3.0/pygls/workspace/position_codec.py000066400000000000000000000175231455570166200214030ustar00rootroot00000000000000############################################################################ # Original work Copyright 2017 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import logging from typing import List, Optional, Union from lsprotocol import types log = logging.getLogger(__name__) class PositionCodec: def __init__( self, encoding: Optional[ Union[types.PositionEncodingKind, str] ] = types.PositionEncodingKind.Utf16, ): self.encoding = encoding @classmethod def is_char_beyond_multilingual_plane(cls, char: str) -> bool: return ord(char) > 0xFFFF def utf16_unit_offset(self, chars: str): """ Calculate the number of characters which need two utf-16 code units. Arguments: chars (str): The string to count occurrences of utf-16 code units for. """ return sum(self.is_char_beyond_multilingual_plane(ch) for ch in chars) def client_num_units(self, chars: str): """ Calculate the length of `str` in client-supported UTF-[32|16|8] code units. Arguments: chars (str): The string to return the length in UTF-[32|16|8] code units for. """ utf32_units = len(chars) if self.encoding == types.PositionEncodingKind.Utf32: return utf32_units if self.encoding == types.PositionEncodingKind.Utf8: return utf32_units + (self.utf16_unit_offset(chars) * 2) return utf32_units + self.utf16_unit_offset(chars) def position_from_client_units( self, lines: List[str], position: types.Position ) -> types.Position: """ Convert the position.character from UTF-[32|16|8] code units to UTF-32. A python application can't use the character member of `Position` directly. As per specification it is represented as a zero-based line and character offset based on posible a UTF-[32|16|8] string representation. All characters whose code point exceeds the Basic Multilingual Plane are represented by 2 UTF-16 or 4 UTF-8 code units. The offset of the closing quotation mark in x="đź‹" is - 7 in UTF-8 representation - 5 in UTF-16 representation - 4 in UTF-32 representation see: https://github.com/microsoft/language-server-protocol/issues/376 Arguments: lines (list): The content of the document which the position refers to. position (Position): The line and character offset in UTF-[32|16|8] code units. Returns: The position with `character` being converted to UTF-32 code units. """ if len(lines) == 0: return types.Position(0, 0) if position.line >= len(lines): return types.Position(len(lines) - 1, self.client_num_units(lines[-1])) _line = lines[position.line] _line = _line.replace("\r\n", "\n") # TODO: it's a bit of a hack _client_len = self.client_num_units(_line) _utf32_len = len(_line) if _client_len == 0: return types.Position(position.line, 0) _client_end_of_line = self.client_num_units(_line) if position.character > _client_end_of_line: position.character = _client_end_of_line - 1 _client_index = 0 utf32_index = 0 while True: _is_searching_queried_position = _client_index < position.character _is_before_end_of_line = utf32_index < _utf32_len _is_searching_for_position = ( _is_searching_queried_position and _is_before_end_of_line ) if not _is_searching_for_position: break _current_char = _line[utf32_index] _is_double_width = PositionCodec.is_char_beyond_multilingual_plane( _current_char ) if _is_double_width: if self.encoding == types.PositionEncodingKind.Utf32: _client_index += 1 if self.encoding == types.PositionEncodingKind.Utf8: _client_index += 4 _client_index += 2 else: _client_index += 1 utf32_index += 1 position = types.Position(line=position.line, character=utf32_index) return position def position_to_client_units( self, lines: List[str], position: types.Position ) -> types.Position: """ Convert the position.character from its internal UTF-32 representation to client-supported UTF-[32|16|8] code units. Arguments: lines (list): The content of the document which the position refers to. position (Position): The line and character offset in UTF-32 code units. Returns: The position with `character` being converted to UTF-[32|16|8] code units. """ try: character = self.client_num_units( lines[position.line][: position.character] ) return types.Position( line=position.line, character=character, ) except IndexError: return types.Position(line=len(lines), character=0) def range_from_client_units( self, lines: List[str], range: types.Range ) -> types.Range: """ Convert range.[start|end].character from UTF-[32|16|8] code units to UTF-32. Arguments: lines (list): The content of the document which the range refers to. range (Range): The line and character offset in UTF-[32|16|8] code units. Returns: The range with `character` offsets being converted to UTF-32 code units. """ range_new = types.Range( start=self.position_from_client_units(lines, range.start), end=self.position_from_client_units(lines, range.end), ) return range_new def range_to_client_units( self, lines: List[str], range: types.Range ) -> types.Range: """ Convert range.[start|end].character from UTF-32 to UTF-[32|16|8] code units. Arguments: lines (list): The content of the document which the range refers to. range (Range): The line and character offset in code units. Returns: The range with `character` offsets being converted to UTF-[32|16|8] code units. """ return types.Range( start=self.position_to_client_units(lines, range.start), end=self.position_to_client_units(lines, range.end), ) pygls-1.3.0/pygls/workspace/text_document.py000066400000000000000000000215071455570166200212610ustar00rootroot00000000000000############################################################################ # Original work Copyright 2017 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import io import logging import os import re from typing import List, Optional, Pattern from lsprotocol import types from pygls.uris import to_fs_path from .position_codec import PositionCodec # TODO: this is not the best e.g. we capture numbers RE_END_WORD = re.compile("^[A-Za-z_0-9]*") RE_START_WORD = re.compile("[A-Za-z_0-9]*$") logger = logging.getLogger(__name__) class TextDocument(object): def __init__( self, uri: str, source: Optional[str] = None, version: Optional[int] = None, language_id: Optional[str] = None, local: bool = True, sync_kind: types.TextDocumentSyncKind = types.TextDocumentSyncKind.Incremental, position_codec: Optional[PositionCodec] = None, ): self.uri = uri self.version = version path = to_fs_path(uri) if path is None: raise Exception("`path` cannot be None") self.path = path self.language_id = language_id self.filename: Optional[str] = os.path.basename(self.path) self._local = local self._source = source self._is_sync_kind_full = sync_kind == types.TextDocumentSyncKind.Full self._is_sync_kind_incremental = ( sync_kind == types.TextDocumentSyncKind.Incremental ) self._is_sync_kind_none = sync_kind == types.TextDocumentSyncKind.None_ self._position_codec = position_codec if position_codec else PositionCodec() def __str__(self): return str(self.uri) @property def position_codec(self) -> PositionCodec: return self._position_codec def _apply_incremental_change( self, change: types.TextDocumentContentChangeEvent_Type1 ) -> None: """Apply an ``Incremental`` text change to the document""" lines = self.lines text = change.text change_range = change.range range = self._position_codec.range_from_client_units(lines, change_range) start_line = range.start.line start_col = range.start.character end_line = range.end.line end_col = range.end.character # Check for an edit occurring at the very end of the file if start_line == len(lines): self._source = self.source + text return new = io.StringIO() # Iterate over the existing document until we hit the edit range, # at which point we write the new text, then loop until we hit # the end of the range and continue writing. for i, line in enumerate(lines): if i < start_line: new.write(line) continue if i > end_line: new.write(line) continue if i == start_line: new.write(line[:start_col]) new.write(text) if i == end_line: new.write(line[end_col:]) self._source = new.getvalue() def _apply_full_change(self, change: types.TextDocumentContentChangeEvent) -> None: """Apply a ``Full`` text change to the document.""" self._source = change.text def _apply_none_change(self, _: types.TextDocumentContentChangeEvent) -> None: """Apply a ``None`` text change to the document Currently does nothing, provided for consistency. """ pass def apply_change(self, change: types.TextDocumentContentChangeEvent) -> None: """Apply a text change to a document, considering TextDocumentSyncKind Performs either :attr:`~lsprotocol.types.TextDocumentSyncKind.Incremental`, :attr:`~lsprotocol.types.TextDocumentSyncKind.Full`, or no synchronization based on both the client request and server capabilities. .. admonition:: ``Incremental`` versus ``Full`` synchronization Even if a server accepts ``Incremantal`` SyncKinds, clients may request a ``Full`` SyncKind. In LSP 3.x, clients make this request by omitting both Range and RangeLength from their request. Consequently, the attributes "range" and "rangeLength" will be missing from ``Full`` content update client requests in the pygls Python library. """ if isinstance(change, types.TextDocumentContentChangeEvent_Type1): if self._is_sync_kind_incremental: self._apply_incremental_change(change) return # Log an error, but still perform full update to preserve existing # assumptions in test_document/test_document_full_edit. Test breaks # otherwise, and fixing the tests would require a broader fix to # protocol.py. logger.error( "Unsupported client-provided TextDocumentContentChangeEvent. " "Please update / submit a Pull Request to your LSP client." ) if self._is_sync_kind_none: self._apply_none_change(change) else: self._apply_full_change(change) @property def lines(self) -> List[str]: return self.source.splitlines(True) def offset_at_position(self, client_position: types.Position) -> int: """Return the character offset pointed at by the given client_position.""" lines = self.lines server_position = self._position_codec.position_from_client_units( lines, client_position ) row, col = server_position.line, server_position.character return col + sum( self._position_codec.client_num_units(line) for line in lines[:row] ) @property def source(self) -> str: if self._source is None: with io.open(self.path, "r", encoding="utf-8") as f: return f.read() return self._source def word_at_position( self, client_position: types.Position, re_start_word: Pattern[str] = RE_START_WORD, re_end_word: Pattern[str] = RE_END_WORD, ) -> str: """Return the word at position. The word is constructed in two halves, the first half is found by taking the first match of ``re_start_word`` on the line up until ``position.character``. The second half is found by taking ``position.character`` up until the last match of ``re_end_word`` on the line. :func:`python:re.findall` is used to find the matches. Parameters ---------- position The line and character offset. re_start_word The regular expression for extracting the word backward from position. The default pattern is ``[A-Za-z_0-9]*$``. re_end_word The regular expression for extracting the word forward from position. The default pattern is ``^[A-Za-z_0-9]*``. Returns ------- str The word (obtained by concatenating the two matches) at position. """ lines = self.lines if client_position.line >= len(lines): return "" server_position = self._position_codec.position_from_client_units( lines, client_position ) row, col = server_position.line, server_position.character line = lines[row] # Split word in two start = line[:col] end = line[col:] # Take end of start and start of end to find word # These are guaranteed to match, even if they match the empty string m_start = re_start_word.findall(start) m_end = re_end_word.findall(end) return m_start[0] + m_end[-1] pygls-1.3.0/pygls/workspace/workspace.py000066400000000000000000000264441455570166200204020ustar00rootroot00000000000000############################################################################ # Original work Copyright 2017 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import copy import logging import os import warnings from typing import Dict, List, Optional, Union from lsprotocol import types from lsprotocol.types import ( PositionEncodingKind, TextDocumentSyncKind, WorkspaceFolder, ) from pygls.uris import to_fs_path, uri_scheme from pygls.workspace.text_document import TextDocument from pygls.workspace.position_codec import PositionCodec logger = logging.getLogger(__name__) class Workspace(object): def __init__( self, root_uri: Optional[str], sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental, workspace_folders: Optional[List[WorkspaceFolder]] = None, position_encoding: Optional[ Union[PositionEncodingKind, str] ] = PositionEncodingKind.Utf16, ): self._root_uri = root_uri if self._root_uri is not None: self._root_uri_scheme = uri_scheme(self._root_uri) root_path = to_fs_path(self._root_uri) if root_path is None: raise Exception("Couldn't get `root_path` from `root_uri`") self._root_path = root_path else: self._root_path = None self._sync_kind = sync_kind self._text_documents: Dict[str, TextDocument] = {} self._notebook_documents: Dict[str, types.NotebookDocument] = {} # Used to lookup notebooks which contain a given cell. self._cell_in_notebook: Dict[str, str] = {} self._folders: Dict[str, WorkspaceFolder] = {} self._docs: Dict[str, TextDocument] = {} self._position_encoding = position_encoding self._position_codec = PositionCodec(encoding=position_encoding) if workspace_folders is not None: for folder in workspace_folders: self.add_folder(folder) @property def position_encoding(self) -> Optional[Union[PositionEncodingKind, str]]: return self._position_encoding @property def position_codec(self) -> PositionCodec: return self._position_codec def _create_text_document( self, doc_uri: str, source: Optional[str] = None, version: Optional[int] = None, language_id: Optional[str] = None, ) -> TextDocument: return TextDocument( doc_uri, source=source, version=version, language_id=language_id, sync_kind=self._sync_kind, position_codec=self._position_codec, ) def add_folder(self, folder: WorkspaceFolder): self._folders[folder.uri] = folder @property def documents(self): warnings.warn( "'workspace.documents' has been deprecated, use " "'workspace.text_documents' instead", DeprecationWarning, stacklevel=2, ) return self.text_documents @property def notebook_documents(self): return self._notebook_documents @property def text_documents(self): return self._text_documents @property def folders(self): return self._folders def get_notebook_document( self, *, notebook_uri: Optional[str] = None, cell_uri: Optional[str] = None ) -> Optional[types.NotebookDocument]: """Return the notebook corresponding with the given uri. If both ``notebook_uri`` and ``cell_uri`` are given, ``notebook_uri`` takes precedence. Parameters ---------- notebook_uri If given, return the notebook document with the given uri. cell_uri If given, return the notebook document which contains a cell with the given uri Returns ------- Optional[NotebookDocument] The requested notebook document if found, ``None`` otherwise. """ if notebook_uri is not None: return self._notebook_documents.get(notebook_uri) if cell_uri is not None: notebook_uri = self._cell_in_notebook.get(cell_uri) if notebook_uri is None: return None return self._notebook_documents.get(notebook_uri) return None def get_text_document(self, doc_uri: str) -> TextDocument: """ Return a managed document if-present, else create one pointing at disk. See https://github.com/Microsoft/language-server-protocol/issues/177 """ return self._text_documents.get(doc_uri) or self._create_text_document(doc_uri) def is_local(self): return ( self._root_uri_scheme == "" or self._root_uri_scheme == "file" ) and os.path.exists(self._root_path) def put_notebook_document(self, params: types.DidOpenNotebookDocumentParams): notebook = params.notebook_document # Create a fresh instance to ensure our copy cannot be accidentally modified. self._notebook_documents[notebook.uri] = copy.deepcopy(notebook) for cell_document in params.cell_text_documents: self.put_text_document(cell_document, notebook_uri=notebook.uri) def put_text_document( self, text_document: types.TextDocumentItem, notebook_uri: Optional[str] = None, ): """Add a text document to the workspace. Parameters ---------- text_document The text document to add notebook_uri If set, indicates that this text document represents a cell in a notebook document """ doc_uri = text_document.uri self._text_documents[doc_uri] = self._create_text_document( doc_uri, source=text_document.text, version=text_document.version, language_id=text_document.language_id, ) if notebook_uri: self._cell_in_notebook[doc_uri] = notebook_uri def remove_notebook_document(self, params: types.DidCloseNotebookDocumentParams): notebook_uri = params.notebook_document.uri self._notebook_documents.pop(notebook_uri, None) for cell_document in params.cell_text_documents: self.remove_text_document(cell_document.uri) def remove_text_document(self, doc_uri: str): self._text_documents.pop(doc_uri, None) self._cell_in_notebook.pop(doc_uri, None) def remove_folder(self, folder_uri: str): self._folders.pop(folder_uri, None) try: del self._folders[folder_uri] except KeyError: pass @property def root_path(self): return self._root_path @property def root_uri(self): return self._root_uri def update_notebook_document(self, params: types.DidChangeNotebookDocumentParams): uri = params.notebook_document.uri notebook = self._notebook_documents[uri] notebook.version = params.notebook_document.version if params.change.metadata: notebook.metadata = params.change.metadata cell_changes = params.change.cells if cell_changes is None: return # Process changes to any cell metadata. nb_cells = {cell.document: cell for cell in notebook.cells} for new_data in cell_changes.data or []: nb_cell = nb_cells.get(new_data.document) if nb_cell is None: logger.warning( "Ignoring metadata for '%s': not in notebook.", new_data.document ) continue nb_cell.kind = new_data.kind nb_cell.metadata = new_data.metadata nb_cell.execution_summary = new_data.execution_summary # Process changes to the notebook's structure structure = cell_changes.structure if structure: cells = notebook.cells new_cells = structure.array.cells or [] # Re-order the cells before = cells[: structure.array.start] after = cells[(structure.array.start + structure.array.delete_count) :] notebook.cells = [*before, *new_cells, *after] for new_cell in structure.did_open or []: self.put_text_document(new_cell, notebook_uri=uri) for removed_cell in structure.did_close or []: self.remove_text_document(removed_cell.uri) # Process changes to the text content of existing cells. for text in cell_changes.text_content or []: for change in text.changes: self.update_text_document(text.document, change) def update_text_document( self, text_doc: types.VersionedTextDocumentIdentifier, change: types.TextDocumentContentChangeEvent, ): doc_uri = text_doc.uri self._text_documents[doc_uri].apply_change(change) self._text_documents[doc_uri].version = text_doc.version def get_document(self, *args, **kwargs): warnings.warn( "'workspace.get_document' has been deprecated, use " "'workspace.get_text_document' instead", DeprecationWarning, stacklevel=2, ) return self.get_text_document(*args, **kwargs) def remove_document(self, *args, **kwargs): warnings.warn( "'workspace.remove_document' has been deprecated, use " "'workspace.remove_text_document' instead", DeprecationWarning, stacklevel=2, ) return self.remove_text_document(*args, **kwargs) def put_document(self, *args, **kwargs): warnings.warn( "'workspace.put_document' has been deprecated, use " "'workspace.put_text_document' instead", DeprecationWarning, stacklevel=2, ) return self.put_text_document(*args, **kwargs) def update_document(self, *args, **kwargs): warnings.warn( "'workspace.update_document' has been deprecated, use " "'workspace.update_text_document' instead", DeprecationWarning, stacklevel=2, ) return self.update_text_document(*args, **kwargs) pygls-1.3.0/pyproject.toml000066400000000000000000000064151455570166200156060ustar00rootroot00000000000000[tool.poetry] name = "pygls" version = "1.3.0" description = "A pythonic generic language server (pronounced like 'pie glass')" authors = ["Open Law Library "] maintainers = [ "Tom BH ", "Alex Carney ", ] repository = "https://github.com/openlawlibrary/pygls" documentation = "https://pygls.readthedocs.io/en/latest" license = "Apache 2.0" readme = "README.md" # You may want to use the Poetry "Up" plugin to automatically update all dependencies to # their latest major versions. But bear in mind that this is a library, so the non-development # dependency versions will be forced on downstream users. Therefore the very latest versions # may be too restrictive. # See https://github.com/MousaZeidBaker/poetry-plugin-up [tool.poetry.dependencies] python = ">=3.8" cattrs = ">=23.1.2" lsprotocol = "2023.0.1" websockets = { version = ">=11.0.3", optional = true } [tool.poetry.extras] ws = ["websockets"] [tool.poetry.group.dev.dependencies] # Replaces (amongst many other things) flake8 and bandit ruff = ">=0.1.6" # TODO `poethepoet>=0.20` needs python 3.8 poethepoet = ">=0.24.4" mypy = ">=1.7.1" # TODO `black>=23` needs python 3.8 black = ">=23.11.0" [tool.poetry.group.test.dependencies] # Note: `coverage` requires that your Python was built with system `sqlite` development files coverage = { version = ">=7.3.2", extras = ["toml"] } pytest = ">=7.4.3" pytest-asyncio = ">=0.21.0" [tool.poetry.group.docs.dependencies] # TODO `sphinx>=7.26` needs python 3.9 sphinx = ">=7.1.2" sphinx-rtd-theme = ">=1.3.0" [tool.poetry.group.pyodide.dependencies] selenium = "^4.15.2" [tool.pytest.ini_options] asyncio_mode = "auto" [tool.poe.tasks] test-pyodide = "python tests/pyodide_testrunner/run.py" ruff = "ruff check ." mypy = "mypy -p pygls" check_generated_client = "python scripts/check_client_is_uptodate.py" check_commit_style = "npx commitlint --from origin/main --to HEAD --verbose --config commitlintrc.yaml" generate_client = "python scripts/generate_client.py --output pygls/lsp/client.py" generate_contributors_md = "python scripts/generate_contributors_md.py" black_check = "black --check ." poetry_lock_check = "poetry check" [tool.poe.tasks.test] env = { COVERAGE_PROCESS_START = "pyproject.toml" } sequence = [ { cmd = "python -c 'import pathlib,sys; pathlib.Path(f\"{sys.path[-1]}/cov.pth\").write_text(\"import coverage; coverage.process_startup()\");'"}, { cmd = "coverage erase" }, { cmd = "coverage run -m pytest" }, { cmd = "coverage combine" }, { cmd = "coverage report" }, ] ignore_fail = "return_non_zero" [tool.poe.tasks.lint] sequence = [ "ruff", "mypy", "check_generated_client", "check_commit_style", "black_check", "poetry_lock_check" ] ignore_fail = "return_non_zero" [tool.pyright] strict = ["pygls"] [tool.ruff] # Sometimes Black can't reduce line length without breaking more imortant rules. # So allow Ruff to be more lenient. line-length = 120 [tool.black] line-length = 88 extend-exclude = "pygls/lsp/client.py" [tool.coverage.run] parallel = true source_pkgs = ["pygls"] [tool.coverage.report] show_missing = true skip_covered = true sort = "Cover" [tool.mypy] check_untyped_defs = true [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" pygls-1.3.0/scripts/000077500000000000000000000000001455570166200143535ustar00rootroot00000000000000pygls-1.3.0/scripts/check_client_is_uptodate.py000066400000000000000000000007561455570166200217500ustar00rootroot00000000000000import sys import subprocess AUTOGENERATED_CLIENT_FILE = "pygls/lsp/client.py" subprocess.run(["poe", "generate_client"]) result = subprocess.run( ["git", "diff", "--exit-code", AUTOGENERATED_CLIENT_FILE], stdout=subprocess.DEVNULL ) if result.returncode == 0: print("âś… Pygls client is up to date") else: print( "đź”´ Pygls client not uptodate\n" "1. Generate with: `poetry run poe generate_client`\n" "2. Commit" ) sys.exit(result.returncode) pygls-1.3.0/scripts/generate_client.py000066400000000000000000000142701455570166200200610ustar00rootroot00000000000000"""Script to automatically generate a lanaguge client from `lsprotocol` type definitons """ import argparse import inspect import pathlib import re import sys import textwrap from typing import Optional from typing import Set from typing import Tuple from typing import Type from lsprotocol._hooks import _resolve_forward_references from lsprotocol.types import METHOD_TO_TYPES from lsprotocol.types import message_direction cli = argparse.ArgumentParser( description="generate language client from lsprotocol types." ) cli.add_argument("-o", "--output", default=None) def write_imports(imports: Set[Tuple[str, str]]) -> str: lines = [] for import_ in sorted(list(imports), key=lambda i: (i[0], i[1])): if isinstance(import_, tuple): mod, name = import_ lines.append(f"from {mod} import {name}") continue lines.append(f"import {import_}") return "\n".join(lines) def to_snake_case(string: str) -> str: return "".join(f"_{c.lower()}" if c.isupper() else c for c in string) def write_notification( method: str, request: Type, params: Optional[Type], imports: Set[Tuple[str, str]], ) -> str: python_name = to_snake_case(method).replace("/", "_").replace("$_", "") if params is None: param_name = "None" param_mod = "" else: param_mod, param_name = params.__module__, params.__name__ param_mod = param_mod.replace("lsprotocol.types", "types") + "." return "\n".join( [ f"def {python_name}(self, params: {param_mod}{param_name}) -> None:", f' """Send a :lsp:`{method}` notification.', "", textwrap.indent(inspect.getdoc(request) or "", " "), ' """', " if self.stopped:", ' raise RuntimeError("Client has been stopped.")', "", f' self.protocol.notify("{method}", params)', "", ] ) def get_response_type(response: Type, imports: Set[Tuple[str, str]]) -> str: # Find the response type. result_field = [f for f in response.__attrs_attrs__ if f.name == "result"][0] result = re.sub(r"", r"\1", str(result_field.type)) result = re.sub(r"ForwardRef\('([\w.]+)'\)", r"lsprotocol.types.\1", result) result = result.replace("NoneType", "None") # Replace any typing imports with their short name. for match in re.finditer(r"typing.([\w]+)", result): imports.add(("typing", match.group(1))) result = result.replace("lsprotocol.types.", "types.") result = result.replace("typing.", "") return result def write_method( method: str, request: Type, params: Optional[Type], response: Type, imports: Set[Tuple[str, str]], ) -> str: python_name = to_snake_case(method).replace("/", "_").replace("$_", "") if params is None: param_name = "None" param_mod = "" else: param_mod, param_name = params.__module__, params.__name__ param_mod = param_mod.replace("lsprotocol.types", "types") + "." result_type = get_response_type(response, imports) return "\n".join( [ f"def {python_name}(", " self,", f" params: {param_mod}{param_name},", f" callback: Optional[Callable[[{result_type}], None]] = None,", ") -> Future:", f' """Make a :lsp:`{method}` request.', "", textwrap.indent(inspect.getdoc(request) or "", " "), ' """', " if self.stopped:", ' raise RuntimeError("Client has been stopped.")', "", f' return self.protocol.send_request("{method}", params, callback)', "", f"async def {python_name}_async(", " self,", f" params: {param_mod}{param_name},", f") -> {result_type}:", f' """Make a :lsp:`{method}` request.', "", textwrap.indent(inspect.getdoc(request) or "", " "), ' """', " if self.stopped:", ' raise RuntimeError("Client has been stopped.")', "", f' return await self.protocol.send_request_async("{method}", params)', "", ] ) def generate_client() -> str: methods = [] imports = { ("concurrent.futures", "Future"), ("lsprotocol", "types"), ("pygls.protocol", "LanguageServerProtocol"), ("pygls.protocol", "default_converter"), ("pygls.client", "JsonRPCClient"), ("typing", "Callable"), ("typing", "Optional"), } for method_name, types in METHOD_TO_TYPES.items(): # Skip any requests that come from the server. if message_direction(method_name) == "serverToClient": continue request, response, params, _ = types if response is None: method = write_notification(method_name, request, params, imports) else: method = write_method(method_name, request, params, response, imports) methods.append(textwrap.indent(method, " ")) code = [ "# GENERATED FROM scripts/gen-client.py -- DO NOT EDIT", "# flake8: noqa", write_imports(imports), "", "", "class BaseLanguageClient(JsonRPCClient):", "", " def __init__(", " self,", " name: str,", " version: str,", " protocol_cls=LanguageServerProtocol,", " converter_factory=default_converter,", " **kwargs,", " ):", " self.name = name", " self.version = version", " super().__init__(protocol_cls, converter_factory, **kwargs)", "", *methods, ] return "\n".join(code) def main(): args = cli.parse_args() # Make sure all the type annotations in lsprotocol are resolved correctly. _resolve_forward_references() client = generate_client() if args.output is None: sys.stdout.write(client) else: output = pathlib.Path(args.output) output.write_text(client) if __name__ == "__main__": main() pygls-1.3.0/scripts/generate_contributors_md.py000066400000000000000000000032741455570166200220220ustar00rootroot00000000000000""" Example JSON object: { "login": "danixeee", "id": 16227576, "node_id": "MDQ6VXNlcjE2MjI3NTc2", "avatar_url": "https://avatars.githubusercontent.com/u/16227576?v=4", "gravatar_id": "", "url": "https://api.github.com/users/danixeee", "html_url": "https://github.com/danixeee", "followers_url": "https://api.github.com/users/danixeee/followers", "following_url": "https://api.github.com/users/danixeee/following{/other_user}", "gists_url": "https://api.github.com/users/danixeee/gists{/gist_id}", "starred_url": "https://api.github.com/users/danixeee/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/danixeee/subscriptions", "organizations_url": "https://api.github.com/users/danixeee/orgs", "repos_url": "https://api.github.com/users/danixeee/repos", "events_url": "https://api.github.com/users/danixeee/events{/privacy}", "received_events_url": "https://api.github.com/users/danixeee/received_events", "type": "User", "site_admin": false, "contributions": 321 } """ import requests PYGLS_CONTRIBUTORS_JSON_URL = ( "https://api.github.com/repos/openlawlibrary/pygls/contributors" ) CONTRIBUTORS_FILE = "CONTRIBUTORS.md" response = requests.get(PYGLS_CONTRIBUTORS_JSON_URL) contributors = sorted(response.json(), key=lambda d: d["login"].lower()) contents = "# Contributors (contributions)\n" for contributor in contributors: name = contributor["login"] contributions = contributor["contributions"] url = contributor["html_url"] contents += f"* [{name}]({url}) ({contributions})\n" file = open(CONTRIBUTORS_FILE, "w") n = file.write(contents) file.close() print("âś… CONTRIBUTORS.md updated") pygls-1.3.0/tests/000077500000000000000000000000001455570166200140265ustar00rootroot00000000000000pygls-1.3.0/tests/__init__.py000066400000000000000000000031341455570166200161400ustar00rootroot00000000000000############################################################################ # Original work Copyright 2017 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import pytest from pygls import IS_WIN unix_only = pytest.mark.skipif(IS_WIN, reason="Unix only") windows_only = pytest.mark.skipif(not IS_WIN, reason="Windows only") CMD_ASYNC = "cmd_async" CMD_SYNC = "cmd_sync" CMD_THREAD = "cmd_thread" pygls-1.3.0/tests/_init_server_stall_fix_hack.py000066400000000000000000000016451455570166200221310ustar00rootroot00000000000000""" It would be great to find the real underlying issue here, but without these retries we get annoying flakey test errors. So it's preferable to hack this fix to actually guarantee it doesn't generate false negatives in the test suite. """ import os import concurrent RETRIES = 3 def retry_stalled_init_fix_hack(): if "DISABLE_TIMEOUT" in os.environ: return lambda f: f def decorator(func): def newfn(*args, **kwargs): attempt = 0 while attempt < RETRIES: try: return func(*args, **kwargs) except concurrent.futures._base.TimeoutError: print( "\n\nRetrying timeouted test server init " "%d of %d\n" % (attempt, RETRIES) ) attempt += 1 return func(*args, **kwargs) return newfn return decorator pygls-1.3.0/tests/client.py000066400000000000000000000145311455570166200156620ustar00rootroot00000000000000############################################################################ # Original work Copyright 2017 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio import logging import pathlib import sys from concurrent.futures import Future from typing import Dict from typing import List from typing import Type import pytest import pytest_asyncio from lsprotocol import types from pygls import IS_PYODIDE from pygls import uris from pygls.exceptions import JsonRpcMethodNotFound from pygls.lsp.client import BaseLanguageClient from pygls.protocol import LanguageServerProtocol from pygls.protocol import default_converter logger = logging.getLogger(__name__) class LanguageClientProtocol(LanguageServerProtocol): """An extended protocol class with extra methods that are useful for testing.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._notification_futures = {} def _handle_notification(self, method_name, params): if method_name == types.CANCEL_REQUEST: self._handle_cancel_notification(params.id) return future = self._notification_futures.pop(method_name, None) if future: future.set_result(params) try: handler = self._get_handler(method_name) self._execute_notification(handler, params) except (KeyError, JsonRpcMethodNotFound): logger.warning("Ignoring notification for unknown method '%s'", method_name) except Exception: logger.exception( "Failed to handle notification '%s': %s", method_name, params ) def wait_for_notification(self, method: str, callback=None): future: Future = Future() if callback: def wrapper(future: Future): result = future.result() callback(result) future.add_done_callback(wrapper) self._notification_futures[method] = future return future def wait_for_notification_async(self, method: str): future = self.wait_for_notification(method) return asyncio.wrap_future(future) class LanguageClient(BaseLanguageClient): """Language client used to drive test cases.""" def __init__( self, protocol_cls: Type[LanguageClientProtocol] = LanguageClientProtocol, *args, **kwargs, ): super().__init__( "pygls-test-client", "v1", protocol_cls=protocol_cls, *args, **kwargs ) self.diagnostics: Dict[str, List[types.Diagnostic]] = {} """Used to hold any recieved diagnostics.""" self.messages: List[types.ShowMessageParams] = [] """Holds any received ``window/showMessage`` requests.""" self.log_messages: List[types.LogMessageParams] = [] """Holds any received ``window/logMessage`` requests.""" async def wait_for_notification(self, method: str): """Block until a notification with the given method is received. Parameters ---------- method The notification method to wait for, e.g. ``textDocument/publishDiagnostics`` """ return await self.protocol.wait_for_notification_async(method) def make_test_lsp_client() -> LanguageClient: """Construct a new test client instance with the handlers needed to capture additional responses from the server.""" client = LanguageClient(converter_factory=default_converter) @client.feature(types.TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS) def publish_diagnostics( client: LanguageClient, params: types.PublishDiagnosticsParams ): client.diagnostics[params.uri] = params.diagnostics @client.feature(types.WINDOW_LOG_MESSAGE) def log_message(client: LanguageClient, params: types.LogMessageParams): client.log_messages.append(params) levels = ["ERROR: ", "WARNING: ", "INFO: ", "LOG: "] log_level = levels[params.type.value - 1] print(log_level, params.message) @client.feature(types.WINDOW_SHOW_MESSAGE) def show_message(client: LanguageClient, params): client.messages.append(params) return client def create_client_for_server(server_name: str): """Automate the process of creating a language client connected to the given server and tearing it down again. """ @pytest_asyncio.fixture async def fixture_func(): if IS_PYODIDE: pytest.skip("not available in pyodide") client = make_test_lsp_client() server_dir = pathlib.Path(__file__, "..", "..", "examples", "servers").resolve() root_dir = pathlib.Path(__file__, "..", "..", "examples", "workspace").resolve() await client.start_io(sys.executable, str(server_dir / server_name)) # Initialize the server response = await client.initialize_async( types.InitializeParams( capabilities=types.ClientCapabilities(), root_uri=uris.from_fs_path(root_dir), ) ) assert response is not None yield client, response await client.shutdown_async(None) client.exit(None) await client.stop() return fixture_func pygls-1.3.0/tests/conftest.py000066400000000000000000000073401455570166200162310ustar00rootroot00000000000000############################################################################ # Original work Copyright 2017 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio import pathlib import pytest from lsprotocol import types, converters from pygls import uris, IS_PYODIDE from pygls.feature_manager import FeatureManager from pygls.workspace import Workspace from .ls_setup import ( NativeClientServer, PyodideClientServer, setup_ls_features, ) from .client import create_client_for_server DOC = """document for testing with "đź‹" unicode. """ DOC_URI = uris.from_fs_path(__file__) or "" ClientServer = NativeClientServer if IS_PYODIDE: ClientServer = PyodideClientServer @pytest.fixture(autouse=False) def client_server(request): if hasattr(request, "param"): ConfiguredClientServer = request.param client_server = ConfiguredClientServer() else: client_server = ClientServer() setup_ls_features(client_server.server) client_server.start() client, server = client_server yield client, server client_server.stop() @pytest.fixture(scope="session") def uri_for(): """Returns the uri corresponsing to a file in the example workspace.""" base_dir = pathlib.Path( __file__, "..", "..", "examples", "servers", "workspace" ).resolve() def fn(*args): fpath = pathlib.Path(base_dir, *args) return uris.from_fs_path(str(fpath)) return fn @pytest.fixture() def event_loop(): """Redefine `pytest-asyncio's default event_loop fixture to match the scope of our client fixture.""" policy = asyncio.get_event_loop_policy() loop = policy.new_event_loop() yield loop try: # Not implemented on pyodide loop.close() except NotImplementedError: pass @pytest.fixture(scope="session") def server_dir(): """Returns the directory where all the example language servers live""" path = pathlib.Path(__file__) / ".." / ".." / "examples" / "servers" return path.resolve() code_action_client = create_client_for_server("code_actions.py") inlay_hints_client = create_client_for_server("inlay_hints.py") json_server_client = create_client_for_server("json_server.py") @pytest.fixture def feature_manager(): """Return a feature manager""" return FeatureManager(None, converters.get_converter()) @pytest.fixture def workspace(tmpdir): """Return a workspace.""" return Workspace( uris.from_fs_path(str(tmpdir)), sync_kind=types.TextDocumentSyncKind.Incremental, ) pygls-1.3.0/tests/ls_setup.py000066400000000000000000000127621455570166200162460ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio import json import os import threading import pytest from lsprotocol.types import ( EXIT, INITIALIZE, SHUTDOWN, ClientCapabilities, InitializeParams, ) from pygls.server import LanguageServer from . import CMD_ASYNC, CMD_SYNC, CMD_THREAD from ._init_server_stall_fix_hack import retry_stalled_init_fix_hack CALL_TIMEOUT = 3 def setup_ls_features(server): # Commands @server.command(CMD_ASYNC) async def cmd_test3(ls, *args): # pylint: disable=unused-variable return True, threading.get_ident() @server.thread() @server.command(CMD_THREAD) def cmd_test1(ls, *args): # pylint: disable=unused-variable return True, threading.get_ident() @server.command(CMD_SYNC) def cmd_test2(ls, *args): # pylint: disable=unused-variable return True, threading.get_ident() class PyodideTestTransportAdapter: """Transort adapter that's only useful for tests in a pyodide environment.""" def __init__(self, dest: LanguageServer): self.dest = dest def close(self): ... def write(self, data): object_hook = self.dest.lsp._deserialize_message self.dest.lsp._procedure_handler(json.loads(data, object_hook=object_hook)) class PyodideClientServer: """Implementation of the `client_server` fixture for use in a pyodide environment.""" def __init__(self, LS=LanguageServer): self.server = LS("pygls-server", "v1") self.client = LS("pygls-client", "v1") self.server.lsp.connection_made(PyodideTestTransportAdapter(self.client)) self.server.lsp._send_only_body = True self.client.lsp.connection_made(PyodideTestTransportAdapter(self.server)) self.client.lsp._send_only_body = True def start(self): self.initialize() def stop(self): ... @classmethod def decorate(cls): return pytest.mark.parametrize("client_server", [cls], indirect=True) def initialize(self): response = self.client.lsp.send_request( INITIALIZE, InitializeParams( process_id=12345, root_uri="file://", capabilities=ClientCapabilities() ), ).result(timeout=CALL_TIMEOUT) assert response.capabilities is not None def __iter__(self): yield self.client yield self.server class NativeClientServer: def __init__(self, LS=LanguageServer): # Client to Server pipe csr, csw = os.pipe() # Server to client pipe scr, scw = os.pipe() # Setup Server self.server = LS("server", "v1") self.server_thread = threading.Thread( name="Server Thread", target=self.server.start_io, args=(os.fdopen(csr, "rb"), os.fdopen(scw, "wb")), ) self.server_thread.daemon = True # Setup client self.client = LS("client", "v1", asyncio.new_event_loop()) self.client_thread = threading.Thread( name="Client Thread", target=self.client.start_io, args=(os.fdopen(scr, "rb"), os.fdopen(csw, "wb")), ) self.client_thread.daemon = True @classmethod def decorate(cls): return pytest.mark.parametrize("client_server", [cls], indirect=True) def start(self): self.server_thread.start() self.server.thread_id = self.server_thread.ident self.client_thread.start() self.initialize() def stop(self): shutdown_response = self.client.lsp.send_request(SHUTDOWN).result() assert shutdown_response is None self.client.lsp.notify(EXIT) self.server_thread.join() self.client._stop_event.set() try: self.client.loop._signal_handlers.clear() # HACK ? except AttributeError: pass self.client_thread.join() @retry_stalled_init_fix_hack() def initialize(self): timeout = None if "DISABLE_TIMEOUT" in os.environ else 1 response = self.client.lsp.send_request( INITIALIZE, InitializeParams( process_id=12345, root_uri="file://", capabilities=ClientCapabilities() ), ).result(timeout=timeout) assert response.capabilities is not None def __iter__(self): yield self.client yield self.server pygls-1.3.0/tests/lsp/000077500000000000000000000000001455570166200146245ustar00rootroot00000000000000pygls-1.3.0/tests/lsp/__init__.py000066400000000000000000000000001455570166200167230ustar00rootroot00000000000000pygls-1.3.0/tests/lsp/semantic_tokens/000077500000000000000000000000001455570166200200125ustar00rootroot00000000000000pygls-1.3.0/tests/lsp/semantic_tokens/__init__.py000066400000000000000000000000001455570166200221110ustar00rootroot00000000000000pygls-1.3.0/tests/lsp/semantic_tokens/test_delta_missing_legend.py000066400000000000000000000065741455570166200255770ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional, Union from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, ) from lsprotocol.types import ( SemanticTokens, SemanticTokensDeltaParams, SemanticTokensLegend, SemanticTokensPartialResult, SemanticTokensOptionsFullType1, TextDocumentIdentifier, ) from ...conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, SemanticTokensLegend( token_types=["keyword", "operator"], token_modifiers=["readonly"] ), ) def f( params: SemanticTokensDeltaParams, ) -> Union[SemanticTokensPartialResult, Optional[SemanticTokens]]: if params.text_document.uri == "file://return.tokens": return SemanticTokens(data=[0, 0, 3, 0, 0]) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities provider = capabilities.semantic_tokens_provider assert provider.full == SemanticTokensOptionsFullType1(delta=True) assert provider.legend.token_types == [ "keyword", "operator", ] assert provider.legend.token_modifiers == ["readonly"] @ConfiguredLS.decorate() def test_semantic_tokens_full_delta_return_tokens(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, SemanticTokensDeltaParams( text_document=TextDocumentIdentifier(uri="file://return.tokens"), previous_result_id="id", ), ).result() assert response assert response.data == [0, 0, 3, 0, 0] @ConfiguredLS.decorate() def test_semantic_tokens_full_delta_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, SemanticTokensDeltaParams( text_document=TextDocumentIdentifier(uri="file://return.none"), previous_result_id="id", ), ).result() assert response is None pygls-1.3.0/tests/lsp/semantic_tokens/test_delta_missing_legend_none.py000066400000000000000000000040761455570166200266110ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional, Union from lsprotocol.types import ( SemanticTokens, SemanticTokensDeltaParams, SemanticTokensPartialResult, ) from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, ) from ...conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature(TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA) def f( params: SemanticTokensDeltaParams, ) -> Union[SemanticTokensPartialResult, Optional[SemanticTokens]]: return SemanticTokens(data=[0, 0, 3, 0, 0]) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.semantic_tokens_provider is None assert capabilities.semantic_tokens_provider is None pygls-1.3.0/tests/lsp/semantic_tokens/test_full_missing_legend.py000066400000000000000000000037561455570166200254470ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional, Union from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, ) from lsprotocol.types import ( SemanticTokens, SemanticTokensPartialResult, SemanticTokensParams, ) from ...conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature(TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL) def f( params: SemanticTokensParams, ) -> Union[SemanticTokensPartialResult, Optional[SemanticTokens]]: return SemanticTokens(data=[0, 0, 3, 0, 0]) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.semantic_tokens_provider is None pygls-1.3.0/tests/lsp/semantic_tokens/test_range.py000066400000000000000000000071031455570166200225200ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional, Union from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, ) from lsprotocol.types import ( Position, Range, SemanticTokens, SemanticTokensLegend, SemanticTokensPartialResult, SemanticTokensRangeParams, TextDocumentIdentifier, ) from ...conftest import ClientServer SemanticTokenReturnType = Optional[ Union[SemanticTokensPartialResult, Optional[SemanticTokens]] ] class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, SemanticTokensLegend( token_types=["keyword", "operator"], token_modifiers=["readonly"] ), ) def f( params: SemanticTokensRangeParams, ) -> SemanticTokenReturnType: if params.text_document.uri == "file://return.tokens": return SemanticTokens(data=[0, 0, 3, 0, 0]) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities provider = capabilities.semantic_tokens_provider assert provider.range assert provider.legend.token_types == [ "keyword", "operator", ] assert provider.legend.token_modifiers == ["readonly"] @ConfiguredLS.decorate() def test_semantic_tokens_range_return_tokens(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, SemanticTokensRangeParams( text_document=TextDocumentIdentifier(uri="file://return.tokens"), range=Range( start=Position(line=0, character=0), end=Position(line=10, character=80), ), ), ).result() assert response assert response.data == [0, 0, 3, 0, 0] @ConfiguredLS.decorate() def test_semantic_tokens_range_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, SemanticTokensRangeParams( text_document=TextDocumentIdentifier(uri="file://return.none"), range=Range( start=Position(line=0, character=0), end=Position(line=10, character=80), ), ), ).result() assert response is None pygls-1.3.0/tests/lsp/semantic_tokens/test_range_missing_legends.py000066400000000000000000000037611455570166200257600ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional, Union from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, ) from lsprotocol.types import ( SemanticTokens, SemanticTokensParams, SemanticTokensPartialResult, ) from ...conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature(TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE) def f( params: SemanticTokensParams, ) -> Union[SemanticTokensPartialResult, Optional[SemanticTokens]]: return SemanticTokens(data=[0, 0, 3, 0, 0]) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.semantic_tokens_provider is None pygls-1.3.0/tests/lsp/semantic_tokens/test_semantic_tokens_full.py000066400000000000000000000063511455570166200256400ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional, Union from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, ) from lsprotocol.types import ( SemanticTokens, SemanticTokensLegend, SemanticTokensParams, SemanticTokensPartialResult, TextDocumentIdentifier, ) from ...conftest import ClientServer SemanticTokenReturnType = Optional[ Union[SemanticTokensPartialResult, Optional[SemanticTokens]] ] class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, SemanticTokensLegend( token_types=["keyword", "operator"], token_modifiers=["readonly"] ), ) def f( params: SemanticTokensParams, ) -> SemanticTokenReturnType: if params.text_document.uri == "file://return.tokens": return SemanticTokens(data=[0, 0, 3, 0, 0]) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities provider = capabilities.semantic_tokens_provider assert provider.full assert provider.legend.token_types == [ "keyword", "operator", ] assert provider.legend.token_modifiers == ["readonly"] @ConfiguredLS.decorate() def test_semantic_tokens_full_return_tokens(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, SemanticTokensParams( text_document=TextDocumentIdentifier(uri="file://return.tokens") ), ).result() assert response assert response.data == [0, 0, 3, 0, 0] @ConfiguredLS.decorate() def test_semantic_tokens_full_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, SemanticTokensParams( text_document=TextDocumentIdentifier(uri="file://return.none") ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_call_hierarchy.py000066400000000000000000000147551455570166200212220ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import ( CALL_HIERARCHY_INCOMING_CALLS, CALL_HIERARCHY_OUTGOING_CALLS, TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, ) from lsprotocol.types import ( CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOptions, CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, Position, Range, SymbolKind, SymbolTag, TextDocumentIdentifier, ) from ..conftest import ClientServer CALL_HIERARCHY_ITEM = CallHierarchyItem( name="test_name", kind=SymbolKind.File, uri="test_uri", range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), selection_range=Range( start=Position(line=1, character=1), end=Position(line=2, character=2), ), tags=[SymbolTag.Deprecated], detail="test_detail", data="test_data", ) def check_call_hierarchy_item_response(item): assert item.name == "test_name" assert item.kind == SymbolKind.File assert item.uri == "test_uri" assert item.range.start.line == 0 assert item.range.start.character == 0 assert item.range.end.line == 1 assert item.range.end.character == 1 assert item.selection_range.start.line == 1 assert item.selection_range.start.character == 1 assert item.selection_range.end.line == 2 assert item.selection_range.end.character == 2 assert len(item.tags) == 1 assert item.tags[0] == SymbolTag.Deprecated assert item.detail == "test_detail" assert item.data == "test_data" class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyOptions(), ) def f1(params: CallHierarchyPrepareParams) -> Optional[List[CallHierarchyItem]]: if params.text_document.uri == "file://return.list": return [CALL_HIERARCHY_ITEM] else: return None @self.server.feature(CALL_HIERARCHY_INCOMING_CALLS) def f2( params: CallHierarchyIncomingCallsParams, ) -> Optional[List[CallHierarchyIncomingCall]]: return [ CallHierarchyIncomingCall( from_=params.item, from_ranges=[ Range( start=Position(line=2, character=2), end=Position(line=3, character=3), ), ], ), ] @self.server.feature(CALL_HIERARCHY_OUTGOING_CALLS) def f3( params: CallHierarchyOutgoingCallsParams, ) -> Optional[List[CallHierarchyOutgoingCall]]: return [ CallHierarchyOutgoingCall( to=params.item, from_ranges=[ Range( start=Position(line=3, character=3), end=Position(line=4, character=4), ), ], ), ] @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.call_hierarchy_provider @ConfiguredLS.decorate() def test_call_hierarchy_prepare_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyPrepareParams( text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), ), ).result() check_call_hierarchy_item_response(response[0]) @ConfiguredLS.decorate() def test_call_hierarchy_prepare_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyPrepareParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None @ConfiguredLS.decorate() def test_call_hierarchy_incoming_calls_return_list(client_server): client, _ = client_server response = client.lsp.send_request( CALL_HIERARCHY_INCOMING_CALLS, CallHierarchyIncomingCallsParams(item=CALL_HIERARCHY_ITEM), ).result() item = response[0] check_call_hierarchy_item_response(item.from_) assert item.from_ranges[0].start.line == 2 assert item.from_ranges[0].start.character == 2 assert item.from_ranges[0].end.line == 3 assert item.from_ranges[0].end.character == 3 @ConfiguredLS.decorate() def test_call_hierarchy_outgoing_calls_return_list(client_server): client, _ = client_server response = client.lsp.send_request( CALL_HIERARCHY_OUTGOING_CALLS, CallHierarchyOutgoingCallsParams(item=CALL_HIERARCHY_ITEM), ).result() item = response[0] check_call_hierarchy_item_response(item.to) assert item.from_ranges[0].start.line == 3 assert item.from_ranges[0].start.character == 3 assert item.from_ranges[0].end.line == 4 assert item.from_ranges[0].end.character == 4 pygls-1.3.0/tests/lsp/test_code_action.py000066400000000000000000000051241455570166200205060ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Tuple from lsprotocol import types from ..client import LanguageClient async def test_code_actions( code_action_client: Tuple[LanguageClient, types.InitializeResult], uri_for ): """Ensure that the example code action server is working as expected.""" client, initialize_result = code_action_client code_action_options = initialize_result.capabilities.code_action_provider assert code_action_options.code_action_kinds == [types.CodeActionKind.QuickFix] test_uri = uri_for("sums.txt") assert test_uri is not None response = await client.text_document_code_action_async( types.CodeActionParams( text_document=types.TextDocumentIdentifier(uri=test_uri), range=types.Range( start=types.Position(line=0, character=0), end=types.Position(line=1, character=0), ), context=types.CodeActionContext(diagnostics=[]), ) ) assert len(response) == 1 code_action = response[0] assert code_action.title == "Evaluate '1 + 1 ='" assert code_action.kind == types.CodeActionKind.QuickFix fix = code_action.edit.changes[test_uri][0] expected_range = types.Range( start=types.Position(line=0, character=0), end=types.Position(line=0, character=7), ) assert fix.range == expected_range assert fix.new_text == "1 + 1 = 2!" pygls-1.3.0/tests/lsp/test_code_lens.py000066400000000000000000000067431455570166200202020ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_CODE_LENS from lsprotocol.types import ( CodeLens, CodeLensOptions, CodeLensParams, Command, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_CODE_LENS, CodeLensOptions(resolve_provider=False), ) def f(params: CodeLensParams) -> Optional[List[CodeLens]]: if params.text_document.uri == "file://return.list": return [ CodeLens( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), command=Command( title="cmd1", command="cmd1", ), data="some data", ), ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.code_lens_provider assert not capabilities.code_lens_provider.resolve_provider @ConfiguredLS.decorate() def test_code_lens_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_CODE_LENS, CodeLensParams(text_document=TextDocumentIdentifier(uri="file://return.list")), ).result() assert response[0].data == "some data" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 assert response[0].command.title == "cmd1" assert response[0].command.command == "cmd1" @ConfiguredLS.decorate() def test_code_lens_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_CODE_LENS, CodeLensParams(text_document=TextDocumentIdentifier(uri="file://return.none")), ).result() assert response is None pygls-1.3.0/tests/lsp/test_color_presentation.py000066400000000000000000000077761455570166200221670ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List from lsprotocol.types import TEXT_DOCUMENT_COLOR_PRESENTATION from lsprotocol.types import ( Color, ColorPresentation, ColorPresentationParams, Position, Range, TextDocumentIdentifier, TextEdit, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature(TEXT_DOCUMENT_COLOR_PRESENTATION) def f(params: ColorPresentationParams) -> List[ColorPresentation]: return [ ColorPresentation( label="label1", text_edit=TextEdit( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), new_text="te", ), additional_text_edits=[ TextEdit( range=Range( start=Position(line=1, character=1), end=Position(line=2, character=2), ), new_text="ate1", ), TextEdit( range=Range( start=Position(line=2, character=2), end=Position(line=3, character=3), ), new_text="ate2", ), ], ) ] @ConfiguredLS.decorate() def test_color_presentation(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_COLOR_PRESENTATION, ColorPresentationParams( text_document=TextDocumentIdentifier(uri="file://return.list"), color=Color(red=0.6, green=0.2, blue=0.3, alpha=0.5), range=Range( start=Position(line=0, character=0), end=Position(line=3, character=3), ), ), ).result() assert response[0].label == "label1" assert response[0].text_edit.new_text == "te" assert response[0].text_edit.range.start.line == 0 assert response[0].text_edit.range.start.character == 0 assert response[0].text_edit.range.end.line == 1 assert response[0].text_edit.range.end.character == 1 range = response[0].additional_text_edits[0].range assert range.start.line == 1 assert range.start.character == 1 assert range.end.line == 2 assert range.end.character == 2 range = response[0].additional_text_edits[1].range assert range.start.line == 2 assert range.start.character == 2 assert range.end.line == 3 assert range.end.character == 3 pygls-1.3.0/tests/lsp/test_completion.py000066400000000000000000000042311455570166200204060ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Tuple from lsprotocol import types from ..client import LanguageClient async def test_completion( json_server_client: Tuple[LanguageClient, types.InitializeResult], uri_for, ): """Ensure that the completion methods are working as expected.""" client, initialize_result = json_server_client completion_provider = initialize_result.capabilities.completion_provider assert completion_provider assert completion_provider.trigger_characters == [","] assert completion_provider.all_commit_characters == [":"] test_uri = uri_for("example.json") assert test_uri is not None response = await client.text_document_completion_async( types.CompletionParams( text_document=types.TextDocumentIdentifier(uri=test_uri), position=types.Position(line=0, character=0), ) ) labels = {i.label for i in response.items} assert labels == set(['"', "[", "]", "{", "}"]) pygls-1.3.0/tests/lsp/test_declaration.py000066400000000000000000000134041455570166200205240ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional, Union from lsprotocol.types import TEXT_DOCUMENT_DECLARATION from lsprotocol.types import ( DeclarationOptions, DeclarationParams, Location, LocationLink, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature(TEXT_DOCUMENT_DECLARATION, DeclarationOptions()) def f( params: DeclarationParams, ) -> Optional[Union[Location, List[Location], List[LocationLink]]]: location = Location( uri="uri", range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ) location_link = LocationLink( target_uri="uri", target_range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), target_selection_range=Range( start=Position(line=0, character=0), end=Position(line=2, character=2), ), origin_selection_range=Range( start=Position(line=0, character=0), end=Position(line=3, character=3), ), ) return { # type: ignore "file://return.location": location, "file://return.location_list": [location], "file://return.location_link_list": [location_link], }.get(params.text_document.uri, None) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.declaration_provider @ConfiguredLS.decorate() def test_declaration_return_location(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DECLARATION, DeclarationParams( text_document=TextDocumentIdentifier(uri="file://return.location"), position=Position(line=0, character=0), ), ).result() assert response.uri == "uri" assert response.range.start.line == 0 assert response.range.start.character == 0 assert response.range.end.line == 1 assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_declaration_return_location_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DECLARATION, DeclarationParams( text_document=TextDocumentIdentifier(uri="file://return.location_list"), position=Position(line=0, character=0), ), ).result() assert response[0].uri == "uri" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_declaration_return_location_link_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DECLARATION, DeclarationParams( text_document=TextDocumentIdentifier( uri="file://return.location_link_list" ), position=Position(line=0, character=0), ), ).result() assert response[0].target_uri == "uri" assert response[0].target_range.start.line == 0 assert response[0].target_range.start.character == 0 assert response[0].target_range.end.line == 1 assert response[0].target_range.end.character == 1 assert response[0].target_selection_range.start.line == 0 assert response[0].target_selection_range.start.character == 0 assert response[0].target_selection_range.end.line == 2 assert response[0].target_selection_range.end.character == 2 assert response[0].origin_selection_range.start.line == 0 assert response[0].origin_selection_range.start.character == 0 assert response[0].origin_selection_range.end.line == 3 assert response[0].origin_selection_range.end.character == 3 @ConfiguredLS.decorate() def test_declaration_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DECLARATION, DeclarationParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_definition.py000066400000000000000000000134401455570166200203670ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional, Union from lsprotocol.types import TEXT_DOCUMENT_DEFINITION from lsprotocol.types import ( DefinitionOptions, DefinitionParams, Location, LocationLink, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_DEFINITION, DefinitionOptions(), ) def f( params: DefinitionParams, ) -> Optional[Union[Location, List[Location], List[LocationLink]]]: location = Location( uri="uri", range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ) location_link = LocationLink( target_uri="uri", target_range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), target_selection_range=Range( start=Position(line=0, character=0), end=Position(line=2, character=2), ), origin_selection_range=Range( start=Position(line=0, character=0), end=Position(line=3, character=3), ), ) return { # type: ignore "file://return.location": location, "file://return.location_list": [location], "file://return.location_link_list": [location_link], }.get(params.text_document.uri, None) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.definition_provider is not None @ConfiguredLS.decorate() def test_definition_return_location(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DEFINITION, DefinitionParams( text_document=TextDocumentIdentifier(uri="file://return.location"), position=Position(line=0, character=0), ), ).result() assert response.uri == "uri" assert response.range.start.line == 0 assert response.range.start.character == 0 assert response.range.end.line == 1 assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_definition_return_location_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DEFINITION, DefinitionParams( text_document=TextDocumentIdentifier(uri="file://return.location_list"), position=Position(line=0, character=0), ), ).result() assert response[0].uri == "uri" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_definition_return_location_link_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DEFINITION, DefinitionParams( text_document=TextDocumentIdentifier( uri="file://return.location_link_list" ), position=Position(line=0, character=0), ), ).result() assert response[0].target_uri == "uri" assert response[0].target_range.start.line == 0 assert response[0].target_range.start.character == 0 assert response[0].target_range.end.line == 1 assert response[0].target_range.end.character == 1 assert response[0].target_selection_range.start.line == 0 assert response[0].target_selection_range.start.character == 0 assert response[0].target_selection_range.end.line == 2 assert response[0].target_selection_range.end.character == 2 assert response[0].origin_selection_range.start.line == 0 assert response[0].origin_selection_range.start.character == 0 assert response[0].origin_selection_range.end.line == 3 assert response[0].origin_selection_range.end.character == 3 @ConfiguredLS.decorate() def test_definition_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DEFINITION, DefinitionParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_diagnostics.py000066400000000000000000000052601455570166200205470ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import json from typing import Tuple from lsprotocol import types from ..client import LanguageClient async def test_diagnostics( json_server_client: Tuple[LanguageClient, types.InitializeResult], uri_for, ): """Ensure that diagnostics are working as expected.""" client, _ = json_server_client test_uri = uri_for("example.json") assert test_uri is not None # Get the expected error message document_content = "text" try: json.loads(document_content) except json.JSONDecodeError as err: expected_message = err.msg client.text_document_did_open( types.DidOpenTextDocumentParams( text_document=types.TextDocumentItem( uri=test_uri, language_id="json", version=1, text=document_content ) ) ) await client.wait_for_notification(types.TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS) diagnostics = client.diagnostics[test_uri] assert diagnostics[0].message == expected_message result = await client.text_document_diagnostic_async( types.DocumentDiagnosticParams( text_document=types.TextDocumentIdentifier(test_uri) ) ) diagnostics = result.items assert diagnostics[0].message == expected_message workspace_result = await client.workspace_diagnostic_async( types.WorkspaceDiagnosticParams(previous_result_ids=[]) ) diagnostics = workspace_result.items[0].items assert diagnostics[0].message == expected_message pygls-1.3.0/tests/lsp/test_document_color.py000066400000000000000000000056631455570166200212630ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_COLOR from lsprotocol.types import ( Color, ColorInformation, DocumentColorOptions, DocumentColorParams, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_DOCUMENT_COLOR, DocumentColorOptions(), ) def f(params: DocumentColorParams) -> List[ColorInformation]: return [ ColorInformation( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), color=Color(red=0.5, green=0.5, blue=0.5, alpha=0.5), ) ] @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.color_provider @ConfiguredLS.decorate() def test_document_color(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DOCUMENT_COLOR, DocumentColorParams( text_document=TextDocumentIdentifier(uri="file://return.list") ), ).result() assert response assert response[0].color.red == 0.5 assert response[0].color.green == 0.5 assert response[0].color.blue == 0.5 assert response[0].color.alpha == 0.5 assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 pygls-1.3.0/tests/lsp/test_document_highlight.py000066400000000000000000000077321455570166200221130ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT from lsprotocol.types import ( DocumentHighlight, DocumentHighlightKind, DocumentHighlightOptions, DocumentHighlightParams, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, DocumentHighlightOptions(), ) def f(params: DocumentHighlightParams) -> Optional[List[DocumentHighlight]]: if params.text_document.uri == "file://return.list": return [ DocumentHighlight( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ), DocumentHighlight( range=Range( start=Position(line=1, character=1), end=Position(line=2, character=2), ), kind=DocumentHighlightKind.Write, ), ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.document_highlight_provider @ConfiguredLS.decorate() def test_document_highlight_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, DocumentHighlightParams( text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), ), ).result() assert response assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 assert response[0].kind is None assert response[1].range.start.line == 1 assert response[1].range.start.character == 1 assert response[1].range.end.line == 2 assert response[1].range.end.character == 2 assert response[1].kind == DocumentHighlightKind.Write @ConfiguredLS.decorate() def test_document_highlight_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, DocumentHighlightParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_document_link.py000066400000000000000000000067771455570166200211110ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_LINK from lsprotocol.types import ( DocumentLink, DocumentLinkOptions, DocumentLinkParams, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_DOCUMENT_LINK, DocumentLinkOptions(resolve_provider=True), ) def f(params: DocumentLinkParams) -> Optional[List[DocumentLink]]: if params.text_document.uri == "file://return.list": return [ DocumentLink( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), target="target", tooltip="tooltip", data="data", ), ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.document_link_provider assert capabilities.document_link_provider.resolve_provider @ConfiguredLS.decorate() def test_document_link_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DOCUMENT_LINK, DocumentLinkParams( text_document=TextDocumentIdentifier(uri="file://return.list"), ), ).result() assert response assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 assert response[0].target == "target" assert response[0].tooltip == "tooltip" assert response[0].data == "data" @ConfiguredLS.decorate() def test_document_link_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DOCUMENT_LINK, DocumentLinkParams( text_document=TextDocumentIdentifier(uri="file://return.none"), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_document_symbol.py000066400000000000000000000142351455570166200214450ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Union from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_SYMBOL from lsprotocol.types import ( DocumentSymbol, DocumentSymbolOptions, DocumentSymbolParams, Location, Position, Range, SymbolInformation, SymbolKind, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_DOCUMENT_SYMBOL, DocumentSymbolOptions(), ) def f( params: DocumentSymbolParams, ) -> Union[List[SymbolInformation], List[DocumentSymbol]]: symbol_info = SymbolInformation( name="symbol", kind=SymbolKind.Namespace, location=Location( uri="uri", range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ), container_name="container", deprecated=False, ) document_symbol_inner = DocumentSymbol( name="inner_symbol", kind=SymbolKind.Number, range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), selection_range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ) document_symbol = DocumentSymbol( name="symbol", kind=SymbolKind.Object, range=Range( start=Position(line=0, character=0), end=Position(line=10, character=10), ), selection_range=Range( start=Position(line=0, character=0), end=Position(line=10, character=10), ), detail="detail", children=[document_symbol_inner], deprecated=True, ) return { # type: ignore "file://return.symbol_information_list": [symbol_info], "file://return.document_symbol_list": [document_symbol], }.get(params.text_document.uri, None) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.document_symbol_provider @ConfiguredLS.decorate() def test_document_symbol_return_symbol_information_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DOCUMENT_SYMBOL, DocumentSymbolParams( text_document=TextDocumentIdentifier( uri="file://return.symbol_information_list" ), ), ).result() assert response assert response[0].name == "symbol" assert response[0].kind == SymbolKind.Namespace assert response[0].location.uri == "uri" assert response[0].location.range.start.line == 0 assert response[0].location.range.start.character == 0 assert response[0].location.range.end.line == 1 assert response[0].location.range.end.character == 1 assert response[0].container_name == "container" assert not response[0].deprecated @ConfiguredLS.decorate() def test_document_symbol_return_document_symbol_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_DOCUMENT_SYMBOL, DocumentSymbolParams( text_document=TextDocumentIdentifier( uri="file://return.document_symbol_list" ), ), ).result() assert response assert response[0].name == "symbol" assert response[0].kind == SymbolKind.Object assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 10 assert response[0].range.end.character == 10 assert response[0].selection_range.start.line == 0 assert response[0].selection_range.start.character == 0 assert response[0].selection_range.end.line == 10 assert response[0].selection_range.end.character == 10 assert response[0].detail == "detail" assert response[0].deprecated assert response[0].children[0].name == "inner_symbol" assert response[0].children[0].kind == SymbolKind.Number assert response[0].children[0].range.start.line == 0 assert response[0].children[0].range.start.character == 0 assert response[0].children[0].range.end.line == 1 assert response[0].children[0].range.end.character == 1 range = response[0].children[0].selection_range assert range.start.line == 0 assert range.start.character == 0 assert range.end.line == 1 assert range.end.character == 1 assert response[0].children[0].children is None pygls-1.3.0/tests/lsp/test_errors.py000066400000000000000000000105751455570166200175610ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Any, List, Union import time import pytest from pygls.exceptions import JsonRpcInternalError, PyglsError, JsonRpcException from lsprotocol.types import WINDOW_SHOW_MESSAGE, MessageType from pygls.server import LanguageServer from ..conftest import ClientServer ERROR_TRIGGER = "test/triggerError" ERROR_MESSAGE = "Testing errors" class CustomLanguageServerSafe(LanguageServer): def report_server_error( self, error: Exception, source: Union[PyglsError, JsonRpcException] ): pass class CustomLanguageServerPotentialRecursion(LanguageServer): def report_server_error( self, error: Exception, source: Union[PyglsError, JsonRpcException] ): raise Exception() class CustomLanguageServerSendAll(LanguageServer): def report_server_error( self, error: Exception, source: Union[PyglsError, JsonRpcException] ): self.show_message(self.default_error_message, msg_type=MessageType.Error) class ConfiguredLS(ClientServer): def __init__(self, LS=LanguageServer): super().__init__(LS) self.init() def init(self): self.client.messages: List[str] = [] @self.server.feature(ERROR_TRIGGER) def f1(params: Any): raise Exception(ERROR_MESSAGE) @self.client.feature(WINDOW_SHOW_MESSAGE) def f2(params: Any): self.client.messages.append(params.message) class CustomConfiguredLSSafe(ConfiguredLS): def __init__(self): super().__init__(CustomLanguageServerSafe) class CustomConfiguredLSPotentialRecusrion(ConfiguredLS): def __init__(self): super().__init__(CustomLanguageServerPotentialRecursion) class CustomConfiguredLSSendAll(ConfiguredLS): def __init__(self): super().__init__(CustomLanguageServerSendAll) @ConfiguredLS.decorate() def test_request_error_reporting_default(client_server): client, _ = client_server assert len(client.messages) == 0 with pytest.raises(JsonRpcInternalError, match=ERROR_MESSAGE): client.lsp.send_request(ERROR_TRIGGER).result() time.sleep(0.1) assert len(client.messages) == 0 @CustomConfiguredLSSendAll.decorate() def test_request_error_reporting_override(client_server): client, _ = client_server assert len(client.messages) == 0 with pytest.raises(JsonRpcInternalError, match=ERROR_MESSAGE): client.lsp.send_request(ERROR_TRIGGER).result() time.sleep(0.1) assert len(client.messages) == 1 @ConfiguredLS.decorate() def test_notification_error_reporting(client_server): client, _ = client_server client.lsp.notify(ERROR_TRIGGER) time.sleep(0.1) assert len(client.messages) == 1 assert client.messages[0] == LanguageServer.default_error_message @CustomConfiguredLSSafe.decorate() def test_overriding_error_reporting(client_server): client, _ = client_server client.lsp.notify(ERROR_TRIGGER) time.sleep(0.1) assert len(client.messages) == 0 @CustomConfiguredLSPotentialRecusrion.decorate() def test_overriding_error_reporting_with_potential_recursion(client_server): client, _ = client_server client.lsp.notify(ERROR_TRIGGER) time.sleep(0.1) assert len(client.messages) == 0 pygls-1.3.0/tests/lsp/test_folding_range.py000066400000000000000000000063471455570166200210450ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_FOLDING_RANGE from lsprotocol.types import ( FoldingRange, FoldingRangeKind, FoldingRangeOptions, FoldingRangeParams, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_FOLDING_RANGE, FoldingRangeOptions(), ) def f(params: FoldingRangeParams) -> Optional[List[FoldingRange]]: if params.text_document.uri == "file://return.list": return [ FoldingRange( start_line=0, end_line=0, start_character=1, end_character=1, kind=FoldingRangeKind.Comment, ), ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.folding_range_provider @ConfiguredLS.decorate() def test_folding_range_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_FOLDING_RANGE, FoldingRangeParams( text_document=TextDocumentIdentifier(uri="file://return.list"), ), ).result() assert response assert response[0].start_line == 0 assert response[0].end_line == 0 assert response[0].start_character == 1 assert response[0].end_character == 1 assert response[0].kind == FoldingRangeKind.Comment @ConfiguredLS.decorate() def test_folding_range_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_FOLDING_RANGE, FoldingRangeParams( text_document=TextDocumentIdentifier(uri="file://return.none"), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_formatting.py000066400000000000000000000074601455570166200204160ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_FORMATTING from lsprotocol.types import ( DocumentFormattingOptions, DocumentFormattingParams, FormattingOptions, Position, Range, TextDocumentIdentifier, TextEdit, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_FORMATTING, DocumentFormattingOptions(), ) def f(params: DocumentFormattingParams) -> Optional[List[TextEdit]]: if params.text_document.uri == "file://return.list": return [ TextEdit( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), new_text="text", ) ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.document_formatting_provider @ConfiguredLS.decorate() def test_document_formatting_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_FORMATTING, DocumentFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.list"), options=FormattingOptions( tab_size=2, insert_spaces=True, trim_trailing_whitespace=True, insert_final_newline=True, trim_final_newlines=True, ), ), ).result() assert response assert response[0].new_text == "text" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_document_formatting_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_FORMATTING, DocumentFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.none"), options=FormattingOptions( tab_size=2, insert_spaces=True, trim_trailing_whitespace=True, insert_final_newline=True, trim_final_newlines=True, ), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_hover.py000066400000000000000000000117151455570166200173650ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional from lsprotocol.types import TEXT_DOCUMENT_HOVER from lsprotocol.types import ( Hover, HoverOptions, HoverParams, MarkedString_Type1, MarkupContent, MarkupKind, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_HOVER, HoverOptions(), ) def f(params: HoverParams) -> Optional[Hover]: range = Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ) return { "file://return.marked_string": Hover( range=range, contents=MarkedString_Type1( language="language", value="value", ), ), "file://return.marked_string_list": Hover( range=range, contents=[ MarkedString_Type1( language="language", value="value", ), "str type", ], ), "file://return.markup_content": Hover( range=range, contents=MarkupContent(kind=MarkupKind.Markdown, value="value"), ), }.get(params.text_document.uri, None) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.hover_provider @ConfiguredLS.decorate() def test_hover_return_marked_string(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_HOVER, HoverParams( text_document=TextDocumentIdentifier(uri="file://return.marked_string"), position=Position(line=0, character=0), ), ).result() assert response assert response.contents.language == "language" assert response.contents.value == "value" assert response.range.start.line == 0 assert response.range.start.character == 0 assert response.range.end.line == 1 assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_hover_return_marked_string_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_HOVER, HoverParams( text_document=TextDocumentIdentifier( uri="file://return.marked_string_list" ), position=Position(line=0, character=0), ), ).result() assert response assert response.contents[0].language == "language" assert response.contents[0].value == "value" assert response.contents[1] == "str type" assert response.range.start.line == 0 assert response.range.start.character == 0 assert response.range.end.line == 1 assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_hover_return_markup_content(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_HOVER, HoverParams( text_document=TextDocumentIdentifier(uri="file://return.markup_content"), position=Position(line=0, character=0), ), ).result() assert response assert response.contents.kind == MarkupKind.Markdown assert response.contents.value == "value" assert response.range.start.line == 0 assert response.range.start.character == 0 assert response.range.end.line == 1 assert response.range.end.character == 1 pygls-1.3.0/tests/lsp/test_implementation.py000066400000000000000000000135441455570166200212710ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional, Union from lsprotocol.types import TEXT_DOCUMENT_IMPLEMENTATION from lsprotocol.types import ( ImplementationOptions, ImplementationParams, Location, LocationLink, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_IMPLEMENTATION, ImplementationOptions(), ) def f( params: ImplementationParams, ) -> Optional[Union[Location, List[Location], List[LocationLink]]]: location = Location( uri="uri", range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ) location_link = LocationLink( target_uri="uri", target_range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), target_selection_range=Range( start=Position(line=0, character=0), end=Position(line=2, character=2), ), origin_selection_range=Range( start=Position(line=0, character=0), end=Position(line=3, character=3), ), ) return { # type: ignore "file://return.location": location, "file://return.location_list": [location], "file://return.location_link_list": [location_link], }.get(params.text_document.uri, None) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.implementation_provider @ConfiguredLS.decorate() def test_type_definition_return_location(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( text_document=TextDocumentIdentifier(uri="file://return.location"), position=Position(line=0, character=0), ), ).result() assert response.uri == "uri" assert response.range.start.line == 0 assert response.range.start.character == 0 assert response.range.end.line == 1 assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_type_definition_return_location_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( text_document=TextDocumentIdentifier(uri="file://return.location_list"), position=Position(line=0, character=0), ), ).result() assert response[0].uri == "uri" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_type_definition_return_location_link_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( text_document=TextDocumentIdentifier( uri="file://return.location_link_list" ), position=Position(line=0, character=0), ), ).result() assert response[0].target_uri == "uri" assert response[0].target_range.start.line == 0 assert response[0].target_range.start.character == 0 assert response[0].target_range.end.line == 1 assert response[0].target_range.end.character == 1 assert response[0].target_selection_range.start.line == 0 assert response[0].target_selection_range.start.character == 0 assert response[0].target_selection_range.end.line == 2 assert response[0].target_selection_range.end.character == 2 assert response[0].origin_selection_range.start.line == 0 assert response[0].origin_selection_range.start.character == 0 assert response[0].origin_selection_range.end.line == 3 assert response[0].origin_selection_range.end.character == 3 @ConfiguredLS.decorate() def test_type_definition_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_inlay_hints.py000066400000000000000000000046111455570166200205600ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Tuple from lsprotocol import types from ..client import LanguageClient async def test_code_actions( inlay_hints_client: Tuple[LanguageClient, types.InitializeResult], uri_for ): """Ensure that the example code action server is working as expected.""" client, initialize_result = inlay_hints_client inlay_hint_provider = initialize_result.capabilities.inlay_hint_provider assert inlay_hint_provider.resolve_provider is True test_uri = uri_for("sums.txt") assert test_uri is not None response = await client.text_document_inlay_hint_async( types.InlayHintParams( text_document=types.TextDocumentIdentifier(uri=test_uri), range=types.Range( start=types.Position(line=3, character=0), end=types.Position(line=4, character=0), ), ) ) assert len(response) == 2 two, three = response[0], response[1] assert two.label == ":10" assert two.tooltip is None assert three.label == ":11" assert three.tooltip is None resolved = await client.inlay_hint_resolve_async(three) assert resolved.tooltip == "Binary representation of the number: 3" pygls-1.3.0/tests/lsp/test_inline_value.py000066400000000000000000000047731455570166200207220ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Tuple from lsprotocol import types from ..client import LanguageClient async def test_inline_value( json_server_client: Tuple[LanguageClient, types.InitializeResult], uri_for, ): """Ensure that inline values are working as expected.""" client, _ = json_server_client test_uri = uri_for("example.json") assert test_uri is not None document_content = '{\n"foo": "bar"\n}' client.text_document_did_open( types.DidOpenTextDocumentParams( text_document=types.TextDocumentItem( uri=test_uri, language_id="json", version=1, text=document_content ) ) ) result = await client.text_document_inline_value_async( types.InlineValueParams( text_document=types.TextDocumentIdentifier(test_uri), range=types.Range( start=types.Position(line=1, character=0), end=types.Position(line=1, character=6), ), context=types.InlineValueContext( frame_id=1, stopped_location=types.Range( start=types.Position(line=1, character=0), end=types.Position(line=1, character=6), ), ), ) ) assert result[0].text == "Inline value" pygls-1.3.0/tests/lsp/test_linked_editing_range.py000066400000000000000000000075321455570166200223710ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional from lsprotocol.types import TEXT_DOCUMENT_LINKED_EDITING_RANGE from lsprotocol.types import ( LinkedEditingRangeOptions, LinkedEditingRangeParams, LinkedEditingRanges, Position, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_LINKED_EDITING_RANGE, LinkedEditingRangeOptions(), ) def f(params: LinkedEditingRangeParams) -> Optional[LinkedEditingRanges]: if params.text_document.uri == "file://return.ranges": return LinkedEditingRanges( ranges=[ Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), Range( start=Position(line=1, character=1), end=Position(line=2, character=2), ), ], word_pattern="pattern", ) else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.linked_editing_range_provider @ConfiguredLS.decorate() def test_linked_editing_ranges_return_ranges(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_LINKED_EDITING_RANGE, LinkedEditingRangeParams( text_document=TextDocumentIdentifier(uri="file://return.ranges"), position=Position(line=0, character=0), ), ).result() assert response assert response.ranges[0].start.line == 0 assert response.ranges[0].start.character == 0 assert response.ranges[0].end.line == 1 assert response.ranges[0].end.character == 1 assert response.ranges[1].start.line == 1 assert response.ranges[1].start.character == 1 assert response.ranges[1].end.line == 2 assert response.ranges[1].end.character == 2 assert response.word_pattern == "pattern" @ConfiguredLS.decorate() def test_linked_editing_ranges_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_LINKED_EDITING_RANGE, LinkedEditingRangeParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_moniker.py000066400000000000000000000064041455570166200177050ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_MONIKER from lsprotocol.types import ( Moniker, MonikerKind, MonikerOptions, MonikerParams, Position, TextDocumentIdentifier, UniquenessLevel, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_MONIKER, MonikerOptions(), ) def f(params: MonikerParams) -> Optional[List[Moniker]]: if params.text_document.uri == "file://return.list": return [ Moniker( scheme="test_scheme", identifier="test_identifier", unique=UniquenessLevel.Global, kind=MonikerKind.Local, ), ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.moniker_provider @ConfiguredLS.decorate() def test_moniker_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_MONIKER, MonikerParams( text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), ), ).result() assert response assert response[0].scheme == "test_scheme" assert response[0].identifier == "test_identifier" assert response[0].unique == UniquenessLevel.Global assert response[0].kind == MonikerKind.Local @ConfiguredLS.decorate() def test_references_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_MONIKER, MonikerParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_on_type_formatting.py000066400000000000000000000105341455570166200221470ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_ON_TYPE_FORMATTING from lsprotocol.types import ( DocumentOnTypeFormattingOptions, DocumentOnTypeFormattingParams, FormattingOptions, Position, Range, TextDocumentIdentifier, TextEdit, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_ON_TYPE_FORMATTING, DocumentOnTypeFormattingOptions( first_trigger_character=":", more_trigger_character=[",", "."], ), ) def f(params: DocumentOnTypeFormattingParams) -> Optional[List[TextEdit]]: if params.text_document.uri == "file://return.list": return [ TextEdit( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), new_text="text", ) ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.document_on_type_formatting_provider assert ( capabilities.document_on_type_formatting_provider.first_trigger_character == ":" ) assert capabilities.document_on_type_formatting_provider.more_trigger_character == [ ",", ".", ] @ConfiguredLS.decorate() def test_on_type_formatting_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_ON_TYPE_FORMATTING, DocumentOnTypeFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), ch=":", options=FormattingOptions( tab_size=2, insert_spaces=True, trim_trailing_whitespace=True, insert_final_newline=True, trim_final_newlines=True, ), ), ).result() assert response assert response[0].new_text == "text" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_on_type_formatting_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_ON_TYPE_FORMATTING, DocumentOnTypeFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ch=":", options=FormattingOptions( tab_size=2, insert_spaces=True, trim_trailing_whitespace=True, insert_final_newline=True, trim_final_newlines=True, ), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_prepare_rename.py000066400000000000000000000076711455570166200212350ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional, Union from lsprotocol.types import TEXT_DOCUMENT_PREPARE_RENAME from lsprotocol.types import ( Position, PrepareRenameResult, PrepareRenameResult_Type1, PrepareRenameParams, Range, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature(TEXT_DOCUMENT_PREPARE_RENAME) def f( params: PrepareRenameParams, ) -> Optional[Union[Range, PrepareRenameResult]]: return { # type: ignore "file://return.range": Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), "file://return.prepare_rename": PrepareRenameResult_Type1( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), placeholder="placeholder", ), }.get(params.text_document.uri, None) @ConfiguredLS.decorate() def test_capabilities(client_server): pass @ConfiguredLS.decorate() def test_prepare_rename_return_range(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_RENAME, PrepareRenameParams( text_document=TextDocumentIdentifier(uri="file://return.range"), position=Position(line=0, character=0), ), ).result() assert response assert response.start.line == 0 assert response.start.character == 0 assert response.end.line == 1 assert response.end.character == 1 @ConfiguredLS.decorate() def test_prepare_rename_return_prepare_rename(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_RENAME, PrepareRenameParams( text_document=TextDocumentIdentifier(uri="file://return.prepare_rename"), position=Position(line=0, character=0), ), ).result() assert response assert response.range.start.line == 0 assert response.range.start.character == 0 assert response.range.end.line == 1 assert response.range.end.character == 1 assert response.placeholder == "placeholder" @ConfiguredLS.decorate() def test_prepare_rename_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_PREPARE_RENAME, PrepareRenameParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_progress.py000066400000000000000000000175651455570166200201170ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio from typing import List, Optional import pytest from lsprotocol.types import ( TEXT_DOCUMENT_CODE_LENS, WINDOW_WORK_DONE_PROGRESS_CANCEL, WINDOW_WORK_DONE_PROGRESS_CREATE, PROGRESS, ) from lsprotocol.types import ( CodeLens, CodeLensParams, CodeLensOptions, ProgressParams, TextDocumentIdentifier, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressReport, WorkDoneProgressCancelParams, WorkDoneProgressCreateParams, ) from ..conftest import ClientServer from pygls import IS_PYODIDE class ConfiguredLS(ClientServer): def __init__(self): super().__init__() self.client.notifications: List[ProgressParams] = [] self.client.method_calls: List[WorkDoneProgressCreateParams] = [] @self.server.feature( TEXT_DOCUMENT_CODE_LENS, CodeLensOptions(resolve_provider=False, work_done_progress=True), ) async def f1(params: CodeLensParams) -> Optional[List[CodeLens]]: if "client_initiated_token" in params.text_document.uri: token = params.work_done_token else: assert "server_initiated_token" in params.text_document.uri token = params.text_document.uri[len("file://") :] if "async" in params.text_document.uri: await self.server.progress.create_async(token) else: f = self.server.progress.create(token) await asyncio.sleep(0.1) f.result() assert token self.server.lsp.progress.begin( token, WorkDoneProgressBegin(kind="begin", title="starting", percentage=0), ) await asyncio.sleep(0.1) if self.server.lsp.progress.tokens[token].cancelled(): self.server.lsp.progress.end( token, WorkDoneProgressEnd(kind="end", message="cancelled") ) else: self.server.lsp.progress.report( token, WorkDoneProgressReport( kind="report", message="doing", percentage=50 ), ) self.server.lsp.progress.end( token, WorkDoneProgressEnd(kind="end", message="done") ) return None @self.client.feature(PROGRESS) def f2(params): self.client.notifications.append(params) if params.value["kind"] == "begin" and "cancel" in params.token: # client cancels the progress token self.client.lsp.notify( WINDOW_WORK_DONE_PROGRESS_CANCEL, WorkDoneProgressCancelParams(token=params.token), ) @self.client.feature(WINDOW_WORK_DONE_PROGRESS_CREATE) def f3(params: WorkDoneProgressCreateParams): self.client.method_calls.append(params) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities provider = capabilities.code_lens_provider assert provider assert provider.work_done_progress @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") @ConfiguredLS.decorate() async def test_progress_notifications(client_server): client, _ = client_server client.lsp.send_request( TEXT_DOCUMENT_CODE_LENS, CodeLensParams( text_document=TextDocumentIdentifier(uri="file://client_initiated_token"), work_done_token="token", ), ).result() assert [notif.value for notif in client.notifications] == [ { "kind": "begin", "title": "starting", "percentage": 0, }, { "kind": "report", "message": "doing", "percentage": 50, }, {"kind": "end", "message": "done"}, ] assert {notif.token for notif in client.notifications} == {"token"} @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") @pytest.mark.parametrize("registration", ("sync", "async")) @ConfiguredLS.decorate() async def test_server_initiated_progress_notifications(client_server, registration): client, _ = client_server client.lsp.send_request( TEXT_DOCUMENT_CODE_LENS, CodeLensParams( text_document=TextDocumentIdentifier( uri=f"file://server_initiated_token_{registration}" ), work_done_token="token", ), ).result() assert [notif.value for notif in client.notifications] == [ { "kind": "begin", "title": "starting", "percentage": 0, }, { "kind": "report", "message": "doing", "percentage": 50, }, {"kind": "end", "message": "done"}, ] assert {notif.token for notif in client.notifications} == { f"server_initiated_token_{registration}" } assert [mc.token for mc in client.method_calls] == [ f"server_initiated_token_{registration}" ] @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") @ConfiguredLS.decorate() def test_progress_cancel_notifications(client_server): client, _ = client_server client.lsp.send_request( TEXT_DOCUMENT_CODE_LENS, CodeLensParams( text_document=TextDocumentIdentifier(uri="file://client_initiated_token"), work_done_token="token_with_cancellation", ), ).result() assert [notif.value for notif in client.notifications] == [ { "kind": "begin", "title": "starting", "percentage": 0, }, {"kind": "end", "message": "cancelled"}, ] assert {notif.token for notif in client.notifications} == { "token_with_cancellation" } @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") @pytest.mark.parametrize("registration", ("sync", "async")) @ConfiguredLS.decorate() def test_server_initiated_progress_progress_cancel_notifications( client_server, registration ): client, _ = client_server client.lsp.send_request( TEXT_DOCUMENT_CODE_LENS, CodeLensParams( text_document=TextDocumentIdentifier( uri=f"file://server_initiated_token_{registration}_with_cancellation" ), ), ).result() assert [notif.value for notif in client.notifications] == [ { "kind": "begin", "title": "starting", "percentage": 0, }, {"kind": "end", "message": "cancelled"}, ] pygls-1.3.0/tests/lsp/test_range_formatting.py000066400000000000000000000102061455570166200215620ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_RANGE_FORMATTING from lsprotocol.types import ( DocumentRangeFormattingOptions, DocumentRangeFormattingParams, FormattingOptions, Position, Range, TextDocumentIdentifier, TextEdit, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_RANGE_FORMATTING, DocumentRangeFormattingOptions(), ) def f(params: DocumentRangeFormattingParams) -> Optional[List[TextEdit]]: if params.text_document.uri == "file://return.list": return [ TextEdit( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), new_text="text", ) ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.document_range_formatting_provider @ConfiguredLS.decorate() def test_range_formatting_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_RANGE_FORMATTING, DocumentRangeFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.list"), range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), options=FormattingOptions( tab_size=2, insert_spaces=True, trim_trailing_whitespace=True, insert_final_newline=True, trim_final_newlines=True, ), ), ).result() assert response assert response[0].new_text == "text" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_range_formatting_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_RANGE_FORMATTING, DocumentRangeFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.none"), range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), options=FormattingOptions( tab_size=2, insert_spaces=True, trim_trailing_whitespace=True, insert_final_newline=True, trim_final_newlines=True, ), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_references.py000066400000000000000000000070061455570166200203610ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_REFERENCES from lsprotocol.types import ( Location, Position, Range, ReferenceContext, ReferenceOptions, ReferenceParams, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_REFERENCES, ReferenceOptions(), ) def f(params: ReferenceParams) -> Optional[List[Location]]: if params.text_document.uri == "file://return.list": return [ Location( uri="uri", range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ), ] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.references_provider @ConfiguredLS.decorate() def test_references_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_REFERENCES, ReferenceParams( text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), context=ReferenceContext( include_declaration=True, ), ), ).result() assert response assert response[0].uri == "uri" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_references_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_REFERENCES, ReferenceParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), context=ReferenceContext( include_declaration=True, ), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_rename.py000066400000000000000000000146751455570166200175210ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional from lsprotocol.types import TEXT_DOCUMENT_RENAME from lsprotocol.types import ( CreateFile, CreateFileOptions, DeleteFile, DeleteFileOptions, OptionalVersionedTextDocumentIdentifier, Position, Range, RenameFile, RenameFileOptions, RenameOptions, RenameParams, ResourceOperationKind, TextDocumentEdit, TextDocumentIdentifier, TextEdit, WorkspaceEdit, ) from ..conftest import ClientServer workspace_edit = { "changes": { "uri1": [ TextEdit( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), new_text="text1", ), TextEdit( range=Range( start=Position(line=1, character=1), end=Position(line=2, character=2), ), new_text="text2", ), ], }, "document_changes": [ TextDocumentEdit( text_document=OptionalVersionedTextDocumentIdentifier( uri="uri", version=3, ), edits=[ TextEdit( range=Range( start=Position(line=2, character=2), end=Position(line=3, character=3), ), new_text="text3", ), ], ), CreateFile( kind=ResourceOperationKind.Create.value, uri="create file", options=CreateFileOptions( overwrite=True, ignore_if_exists=True, ), ), RenameFile( kind=ResourceOperationKind.Rename.value, old_uri="rename old uri", new_uri="rename new uri", options=RenameFileOptions( overwrite=True, ignore_if_exists=True, ), ), DeleteFile( kind=ResourceOperationKind.Delete.value, uri="delete file", options=DeleteFileOptions( recursive=True, ignore_if_not_exists=True, ), ), ], } class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_RENAME, RenameOptions(prepare_provider=True), ) def f(params: RenameParams) -> Optional[WorkspaceEdit]: if params.text_document.uri == "file://return.workspace_edit": return WorkspaceEdit(**workspace_edit) else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.rename_provider assert capabilities.rename_provider.prepare_provider @ConfiguredLS.decorate() def test_rename_return_workspace_edit(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_RENAME, RenameParams( text_document=TextDocumentIdentifier(uri="file://return.workspace_edit"), position=Position(line=0, character=0), new_name="new name", ), ).result() assert response changes = response.changes["uri1"] assert changes[0].new_text == "text1" assert changes[0].range.start.line == 0 assert changes[0].range.start.character == 0 assert changes[0].range.end.line == 1 assert changes[0].range.end.character == 1 assert changes[1].new_text == "text2" assert changes[1].range.start.line == 1 assert changes[1].range.start.character == 1 assert changes[1].range.end.line == 2 assert changes[1].range.end.character == 2 changes = response.document_changes assert changes[0].text_document.uri == "uri" assert changes[0].text_document.version == 3 assert changes[0].edits[0].new_text == "text3" assert changes[0].edits[0].range.start.line == 2 assert changes[0].edits[0].range.start.character == 2 assert changes[0].edits[0].range.end.line == 3 assert changes[0].edits[0].range.end.character == 3 assert changes[1].kind == ResourceOperationKind.Create.value assert changes[1].uri == "create file" assert changes[1].options.ignore_if_exists assert changes[1].options.overwrite assert changes[2].kind == ResourceOperationKind.Rename.value assert changes[2].new_uri == "rename new uri" assert changes[2].old_uri == "rename old uri" assert changes[2].options.ignore_if_exists assert changes[2].options.overwrite assert changes[3].kind == ResourceOperationKind.Delete.value assert changes[3].uri == "delete file" assert changes[3].options.ignore_if_not_exists assert changes[3].options.recursive @ConfiguredLS.decorate() def test_rename_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_RENAME, RenameParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), new_name="new name", ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_selection_range.py000066400000000000000000000075741455570166200214130ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol.types import TEXT_DOCUMENT_SELECTION_RANGE from lsprotocol.types import ( Position, Range, SelectionRange, SelectionRangeOptions, SelectionRangeParams, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_SELECTION_RANGE, SelectionRangeOptions(), ) def f(params: SelectionRangeParams) -> Optional[List[SelectionRange]]: if params.text_document.uri == "file://return.list": root = SelectionRange( range=Range( start=Position(line=0, character=0), end=Position(line=10, character=10), ), ) inner_range = SelectionRange( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), parent=root, ) return [root, inner_range] else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.selection_range_provider @ConfiguredLS.decorate() def test_selection_range_return_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SELECTION_RANGE, SelectionRangeParams( # query="query", text_document=TextDocumentIdentifier(uri="file://return.list"), positions=[Position(line=0, character=0)], ), ).result() assert response root = response[0] assert root.range.start.line == 0 assert root.range.start.character == 0 assert root.range.end.line == 10 assert root.range.end.character == 10 assert root.parent is None assert response[1].range.start.line == 0 assert response[1].range.start.character == 0 assert response[1].range.end.line == 1 assert response[1].range.end.character == 1 assert response[1].parent == root @ConfiguredLS.decorate() def test_selection_range_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SELECTION_RANGE, SelectionRangeParams( # query="query", text_document=TextDocumentIdentifier(uri="file://return.none"), positions=[Position(line=0, character=0)], ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_signature_help.py000066400000000000000000000136701455570166200212550ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import Optional import pytest from lsprotocol.types import TEXT_DOCUMENT_SIGNATURE_HELP from lsprotocol.types import ( ParameterInformation, Position, SignatureHelp, SignatureHelpContext, SignatureHelpOptions, SignatureHelpParams, SignatureHelpTriggerKind, SignatureInformation, TextDocumentIdentifier, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_SIGNATURE_HELP, SignatureHelpOptions( trigger_characters=["a", "b"], retrigger_characters=["c", "d"], ), ) def f(params: SignatureHelpParams) -> Optional[SignatureHelp]: if params.text_document.uri == "file://return.signature_help": return SignatureHelp( signatures=[ SignatureInformation( label="label", documentation="documentation", parameters=[ ParameterInformation( label=(0, 0), documentation="documentation", ), ], ), ], active_signature=0, active_parameter=0, ) else: return None @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities provider = capabilities.signature_help_provider assert provider assert provider.trigger_characters == ["a", "b"] assert provider.retrigger_characters == ["c", "d"] @ConfiguredLS.decorate() @pytest.mark.skip def test_signature_help_return_signature_help(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SIGNATURE_HELP, SignatureHelpParams( text_document=TextDocumentIdentifier(uri="file://return.signature_help"), position=Position(line=0, character=0), context=SignatureHelpContext( trigger_kind=SignatureHelpTriggerKind.TriggerCharacter, is_retrigger=True, trigger_character="a", active_signature_help=SignatureHelp( signatures=[ SignatureInformation( label="label", documentation="documentation", parameters=[ ParameterInformation( label=(0, 0), documentation="documentation", ), ], ), ], active_signature=0, active_parameter=0, ), ), ), ).result() assert response assert response["activeParameter"] == 0 assert response["activeSignature"] == 0 assert response["signatures"][0]["label"] == "label" assert response["signatures"][0]["documentation"] == "documentation" assert response["signatures"][0]["parameters"][0]["label"] == [0, 0] assert ( response["signatures"][0]["parameters"][0]["documentation"] == "documentation" ) @ConfiguredLS.decorate() @pytest.mark.skip def test_signature_help_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_SIGNATURE_HELP, SignatureHelpParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), context=SignatureHelpContext( trigger_kind=SignatureHelpTriggerKind.TriggerCharacter, is_retrigger=True, trigger_character="a", active_signature_help=SignatureHelp( signatures=[ SignatureInformation( label="label", documentation="documentation", parameters=[ ParameterInformation( label=(0, 0), documentation="documentation", ), ], ), ], active_signature=0, active_parameter=0, ), ), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_type_definition.py000066400000000000000000000135521455570166200214340ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional, Union from lsprotocol.types import TEXT_DOCUMENT_TYPE_DEFINITION from lsprotocol.types import ( Location, LocationLink, Position, Range, TextDocumentIdentifier, TypeDefinitionOptions, TypeDefinitionParams, ) from ..conftest import ClientServer class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature( TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionOptions(), ) def f( params: TypeDefinitionParams, ) -> Optional[Union[Location, List[Location], List[LocationLink]]]: location = Location( uri="uri", range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ) location_link = LocationLink( target_uri="uri", target_range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), target_selection_range=Range( start=Position(line=0, character=0), end=Position(line=2, character=2), ), origin_selection_range=Range( start=Position(line=0, character=0), end=Position(line=3, character=3), ), ) return { # type: ignore "file://return.location": location, "file://return.location_list": [location], "file://return.location_link_list": [location_link], }.get(params.text_document.uri, None) @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.type_definition_provider @ConfiguredLS.decorate() def test_type_definition_return_location(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( text_document=TextDocumentIdentifier(uri="file://return.location"), position=Position(line=0, character=0), ), ).result() assert response.uri == "uri" assert response.range.start.line == 0 assert response.range.start.character == 0 assert response.range.end.line == 1 assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_type_definition_return_location_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( text_document=TextDocumentIdentifier(uri="file://return.location_list"), position=Position(line=0, character=0), ), ).result() assert response[0].uri == "uri" assert response[0].range.start.line == 0 assert response[0].range.start.character == 0 assert response[0].range.end.line == 1 assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_type_definition_return_location_link_list(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( text_document=TextDocumentIdentifier( uri="file://return.location_link_list" ), position=Position(line=0, character=0), ), ).result() assert response[0].target_uri == "uri" assert response[0].target_range.start.line == 0 assert response[0].target_range.start.character == 0 assert response[0].target_range.end.line == 1 assert response[0].target_range.end.character == 1 assert response[0].target_selection_range.start.line == 0 assert response[0].target_selection_range.start.character == 0 assert response[0].target_selection_range.end.line == 2 assert response[0].target_selection_range.end.character == 2 assert response[0].origin_selection_range.start.line == 0 assert response[0].origin_selection_range.start.character == 0 assert response[0].origin_selection_range.end.line == 3 assert response[0].origin_selection_range.end.character == 3 @ConfiguredLS.decorate() def test_type_definition_return_none(client_server): client, _ = client_server response = client.lsp.send_request( TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), ), ).result() assert response is None pygls-1.3.0/tests/lsp/test_type_hierarchy.py000066400000000000000000000110471455570166200212570ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from typing import List, Optional from lsprotocol import types as lsp from ..conftest import ClientServer TYPE_HIERARCHY_ITEM = lsp.TypeHierarchyItem( name="test_name", kind=lsp.SymbolKind.Class, uri="test_uri", range=lsp.Range( start=lsp.Position(line=0, character=0), end=lsp.Position(line=0, character=6), ), selection_range=lsp.Range( start=lsp.Position(line=0, character=0), end=lsp.Position(line=0, character=6), ), ) def check_type_hierarchy_item_response(item): assert item.name == TYPE_HIERARCHY_ITEM.name assert item.kind == TYPE_HIERARCHY_ITEM.kind assert item.uri == TYPE_HIERARCHY_ITEM.uri assert item.range == TYPE_HIERARCHY_ITEM.range assert item.selection_range == TYPE_HIERARCHY_ITEM.selection_range class ConfiguredLS(ClientServer): def __init__(self): super().__init__() @self.server.feature(lsp.TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY) def f1( params: lsp.TypeHierarchyPrepareParams, ) -> Optional[List[lsp.TypeHierarchyItem]]: if params.text_document.uri == "file://return.list": return [TYPE_HIERARCHY_ITEM] else: return None @self.server.feature(lsp.TYPE_HIERARCHY_SUPERTYPES) def f2( params: lsp.TypeHierarchySupertypesParams, ) -> Optional[List[lsp.TypeHierarchyItem]]: return [TYPE_HIERARCHY_ITEM] @self.server.feature(lsp.TYPE_HIERARCHY_SUBTYPES) def f3( params: lsp.TypeHierarchySubtypesParams, ) -> Optional[List[lsp.TypeHierarchyItem]]: return [TYPE_HIERARCHY_ITEM] @ConfiguredLS.decorate() def test_capabilities(client_server): _, server = client_server capabilities = server.server_capabilities assert capabilities.type_hierarchy_provider @ConfiguredLS.decorate() def test_type_hierarchy_prepare_return_list(client_server): client, _ = client_server response = client.lsp.send_request( lsp.TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY, lsp.TypeHierarchyPrepareParams( text_document=lsp.TextDocumentIdentifier(uri="file://return.list"), position=lsp.Position(line=0, character=0), ), ).result() check_type_hierarchy_item_response(response[0]) @ConfiguredLS.decorate() def test_type_hierarchy_prepare_return_none(client_server): client, _ = client_server response = client.lsp.send_request( lsp.TEXT_DOCUMENT_PREPARE_TYPE_HIERARCHY, lsp.TypeHierarchyPrepareParams( text_document=lsp.TextDocumentIdentifier(uri="file://return.none"), position=lsp.Position(line=0, character=0), ), ).result() assert response is None @ConfiguredLS.decorate() def test_type_hierarchy_supertypes(client_server): client, _ = client_server response = client.lsp.send_request( lsp.TYPE_HIERARCHY_SUPERTYPES, lsp.TypeHierarchySupertypesParams(item=TYPE_HIERARCHY_ITEM), ).result() check_type_hierarchy_item_response(response[0]) @ConfiguredLS.decorate() def test_type_hierarchy_subtypes(client_server): client, _ = client_server response = client.lsp.send_request( lsp.TYPE_HIERARCHY_SUBTYPES, lsp.TypeHierarchySubtypesParams(item=TYPE_HIERARCHY_ITEM), ).result() check_type_hierarchy_item_response(response[0]) pygls-1.3.0/tests/pyodide_testrunner/000077500000000000000000000000001455570166200177545ustar00rootroot00000000000000pygls-1.3.0/tests/pyodide_testrunner/.gitignore000066400000000000000000000000061455570166200217400ustar00rootroot00000000000000*.whl pygls-1.3.0/tests/pyodide_testrunner/index.html000066400000000000000000000025301455570166200217510ustar00rootroot00000000000000 Pygls Testsuite

    
pygls-1.3.0/tests/pyodide_testrunner/run.py000066400000000000000000000074531455570166200211430ustar00rootroot00000000000000import os import pathlib import shutil import subprocess import sys import tempfile from functools import partial from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer from multiprocessing import Process, Queue from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.common.exceptions import WebDriverException from selenium.webdriver.support import expected_conditions as EC # Path to the root of the repo. REPO = pathlib.Path(__file__).parent.parent.parent BROWSERS = { "chrome": (webdriver.Chrome, webdriver.ChromeOptions), "firefox": (webdriver.Firefox, webdriver.FirefoxOptions), } def build_wheel() -> str: """Build a wheel package of ``pygls`` and its testsuite. In order to test pygls under pyodide, we need to load the code for both pygls and its testsuite. This is done by building a wheel. To avoid messing with the repo this is all done under a temp directory. """ with tempfile.TemporaryDirectory() as tmpdir: # Copy all required files. dest = pathlib.Path(tmpdir) # So that we don't have to fuss with packaging, copy the test suite into `pygls` # as a sub module. directories = [("pygls", "pygls"), ("tests", "pygls/tests")] for src, target in directories: shutil.copytree(REPO / src, dest / target) files = ["pyproject.toml", "poetry.lock", "README.md", "ThirdPartyNotices.txt"] for src in files: shutil.copy(REPO / src, dest) # Convert the lock file to requirements.txt. # Ensures reproducible behavour for testing. subprocess.run( [ "poetry", "export", "-f", "requirements.txt", "--output", "requirements.txt", ], cwd=dest, ) subprocess.run( ["poetry", "run", "pip", "install", "-r", "requirements.txt"], cwd=dest ) # Build the wheel subprocess.run(["poetry", "build", "--format", "wheel"], cwd=dest) whl = list((dest / "dist").glob("*.whl"))[0] shutil.copy(whl, REPO / "tests/pyodide_testrunner") return whl.name def spawn_http_server(q: Queue, directory: str): """A http server is needed to serve the files to the browser.""" handler_class = partial(SimpleHTTPRequestHandler, directory=directory) server = ThreadingHTTPServer(("localhost", 0), handler_class) q.put(server.server_port) server.serve_forever() def main(): exit_code = 1 whl = build_wheel() q = Queue() server_process = Process( target=spawn_http_server, args=(q, REPO / "tests/pyodide_testrunner"), daemon=True, ) server_process.start() port = q.get() print("Running tests...") try: driver_cls, options_cls = BROWSERS[os.environ.get("BROWSER", "chrome")] options = options_cls() if "CI" in os.environ: options.binary_location = "/usr/bin/google-chrome" options.add_argument("--headless") driver = driver_cls(options=options) driver.get(f"http://localhost:{port}?whl={whl}") wait = WebDriverWait(driver, 120) try: button = wait.until(EC.element_to_be_clickable((By.ID, "exit-code"))) exit_code = int(button.text) except WebDriverException as e: print(f"Error while running test: {e!r}") exit_code = 1 console = driver.find_element(By.ID, "console") print(console.text) finally: if hasattr(server_process, "kill"): server_process.kill() else: server_process.terminate() return exit_code if __name__ == "__main__": sys.exit(main()) pygls-1.3.0/tests/pyodide_testrunner/test-runner.js000066400000000000000000000021351455570166200226010ustar00rootroot00000000000000importScripts("https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.js") // Used to redirect pyodide's stdout to the webpage. function patchedStdout(...args) { postMessage(args[0]) } async function runTests(whl) { console.log("Loading pyodide") let pyodide = await loadPyodide({ indexURL: "https://cdn.jsdelivr.net/pyodide/v0.21.3/full/" }) console.log("Installing dependencies") await pyodide.loadPackage("micropip") await pyodide.runPythonAsync(` import sys import micropip await micropip.install('pytest') await micropip.install('pytest-asyncio') await micropip.install('${whl}') `) console.log('Running testsuite') // Patch stdout to redirect the output. pyodide.globals.get('sys').stdout.write = patchedStdout await pyodide.runPythonAsync(` import pytest exit_code = pytest.main(['--color', 'no', '--pyargs', 'pygls.tests']) `) postMessage({ exitCode: pyodide.globals.get('exit_code') }) } let queryParams = new URLSearchParams(self.location.search) runTests(queryParams.get('whl')) pygls-1.3.0/tests/servers/000077500000000000000000000000001455570166200155175ustar00rootroot00000000000000pygls-1.3.0/tests/servers/invalid_json.py000066400000000000000000000010711455570166200205470ustar00rootroot00000000000000"""This server does nothing but print invalid JSON.""" import asyncio import threading import sys from concurrent.futures import ThreadPoolExecutor from pygls.server import aio_readline def handler(data): content = 'Content-Length: 5\r\n\r\n{"ll}'.encode("utf8") sys.stdout.buffer.write(content) sys.stdout.flush() async def main(): await aio_readline( asyncio.get_running_loop(), ThreadPoolExecutor(), threading.Event(), sys.stdin.buffer, handler, ) if __name__ == "__main__": asyncio.run(main()) pygls-1.3.0/tests/servers/large_response.py000066400000000000000000000014031455570166200210770ustar00rootroot00000000000000"""This server returns a particuarly large response.""" import asyncio import threading import sys from concurrent.futures import ThreadPoolExecutor from pygls.server import aio_readline def handler(data): payload = dict( jsonrpc="2.0", id=1, result=dict( numbers=list(range(100_000)), ), ) content = str(payload).replace("'", '"') message = f"Content-Length: {len(content)}\r\n\r\n{content}".encode("utf8") sys.stdout.buffer.write(message) sys.stdout.flush() async def main(): await aio_readline( asyncio.get_running_loop(), ThreadPoolExecutor(), threading.Event(), sys.stdin.buffer, handler, ) if __name__ == "__main__": asyncio.run(main()) pygls-1.3.0/tests/test_client.py000066400000000000000000000074621455570166200167260ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio import pathlib import sys from typing import Union import pytest from pygls import IS_PYODIDE from pygls.client import JsonRPCClient from pygls.exceptions import JsonRpcException, PyglsError SERVERS = pathlib.Path(__file__).parent / "servers" @pytest.mark.asyncio @pytest.mark.skipif(IS_PYODIDE, reason="Subprocesses are not available on pyodide.") async def test_client_detect_server_exit(): """Ensure that the client detects when the server process exits.""" class TestClient(JsonRPCClient): server_exit_called = False async def server_exit(self, server: asyncio.subprocess.Process): self.server_exit_called = True assert server.returncode == 0 client = TestClient() await client.start_io(sys.executable, "-c", "print('Hello, World!')") await asyncio.sleep(1) await client.stop() message = "Expected the `server_exit` method to have been called." assert client.server_exit_called, message @pytest.mark.asyncio @pytest.mark.skipif(IS_PYODIDE, reason="Subprocesses are not available on pyodide.") async def test_client_detect_invalid_json(): """Ensure that the client can detect the case where the server returns invalid json.""" class TestClient(JsonRPCClient): report_error_called = False future = None def report_server_error( self, error: Exception, source: Union[PyglsError, JsonRpcException] ): self.report_error_called = True self.future.cancel() self._server.kill() self._stop_event.set() assert "Unterminated string" in str(error) client = TestClient() await client.start_io(sys.executable, str(SERVERS / "invalid_json.py")) future = client.protocol.send_request_async("method/name", {}) client.future = future try: await future except asyncio.CancelledError: pass # Ignore the exception generated by cancelling the future finally: await client.stop() assert_message = "Expected `report_server_error` to have been called" assert client.report_error_called, assert_message @pytest.mark.asyncio @pytest.mark.skipif(IS_PYODIDE, reason="Subprocesses are not available on pyodide.") async def test_client_large_responses(): """Ensure that the client can correctly handle large responses from a server.""" client = JsonRPCClient() await client.start_io(sys.executable, str(SERVERS / "large_response.py")) result = await client.protocol.send_request_async("get/numbers", {}, msg_id=1) assert len(result.numbers) == 100_000 await client.stop() pygls-1.3.0/tests/test_document.py000066400000000000000000000334321455570166200172620ustar00rootroot00000000000000############################################################################ # Original work Copyright 2018 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import re from lsprotocol import types from pygls.workspace import TextDocument, PositionCodec from .conftest import DOC, DOC_URI def test_document_empty_edit(): doc = TextDocument("file:///uri", "") change = types.TextDocumentContentChangeEvent_Type1( range=types.Range( start=types.Position(line=0, character=0), end=types.Position(line=0, character=0), ), range_length=0, text="f", ) doc.apply_change(change) assert doc.source == "f" def test_document_end_of_file_edit(): old = ["print 'a'\n", "print 'b'\n"] doc = TextDocument("file:///uri", "".join(old)) change = types.TextDocumentContentChangeEvent_Type1( range=types.Range( start=types.Position(line=2, character=0), end=types.Position(line=2, character=0), ), range_length=0, text="o", ) doc.apply_change(change) assert doc.lines == [ "print 'a'\n", "print 'b'\n", "o", ] def test_document_full_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = TextDocument( "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.Full ) change = types.TextDocumentContentChangeEvent_Type1( range=types.Range( start=types.Position(line=1, character=4), end=types.Position(line=2, character=11), ), range_length=0, text="print a, b", ) doc.apply_change(change) assert doc.lines == ["print a, b"] doc = TextDocument( "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.Full ) change = types.TextDocumentContentChangeEvent_Type1( range=types.Range( start=types.Position(line=0, character=0), end=types.Position(line=0, character=0), ), text="print a, b", ) doc.apply_change(change) assert doc.lines == ["print a, b"] def test_document_line_edit(): doc = TextDocument("file:///uri", "itshelloworld") change = types.TextDocumentContentChangeEvent_Type1( range=types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=8), ), range_length=0, text="goodbye", ) doc.apply_change(change) assert doc.source == "itsgoodbyeworld" def test_document_lines(): doc = TextDocument(DOC_URI, DOC) assert len(doc.lines) == 4 assert doc.lines[0] == "document\n" def test_document_multiline_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = TextDocument( "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.Incremental ) change = types.TextDocumentContentChangeEvent_Type1( range=types.Range( start=types.Position(line=1, character=4), end=types.Position(line=2, character=11), ), range_length=0, text="print a, b", ) doc.apply_change(change) assert doc.lines == ["def hello(a, b):\n", " print a, b\n"] doc = TextDocument( "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.Incremental ) change = types.TextDocumentContentChangeEvent_Type1( range=types.Range( start=types.Position(line=1, character=4), end=types.Position(line=2, character=11), ), text="print a, b", ) doc.apply_change(change) assert doc.lines == ["def hello(a, b):\n", " print a, b\n"] def test_document_no_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = TextDocument( "file:///uri", "".join(old), sync_kind=types.TextDocumentSyncKind.None_ ) change = types.TextDocumentContentChangeEvent_Type1( range=types.Range( start=types.Position(line=1, character=4), end=types.Position(line=2, character=11), ), range_length=0, text="print a, b", ) doc.apply_change(change) assert doc.lines == old def test_document_props(): doc = TextDocument(DOC_URI, DOC) assert doc.uri == DOC_URI assert doc.source == DOC def test_document_source_unicode(): document_mem = TextDocument(DOC_URI, "my source") document_disk = TextDocument(DOC_URI) assert isinstance(document_mem.source, type(document_disk.source)) def test_position_from_utf16(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16) assert codec.position_from_client_units( ['x="đź‹"'], types.Position(line=0, character=3) ) == types.Position(line=0, character=3) assert codec.position_from_client_units( ['x="đź‹"'], types.Position(line=0, character=5) ) == types.Position(line=0, character=4) def test_position_from_utf32(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf32) assert codec.position_from_client_units( ['x="đź‹"'], types.Position(line=0, character=3) ) == types.Position(line=0, character=3) assert codec.position_from_client_units( ['x="đź‹"'], types.Position(line=0, character=4) ) == types.Position(line=0, character=4) def test_position_from_utf8(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf8) assert codec.position_from_client_units( ['x="đź‹"'], types.Position(line=0, character=3) ) == types.Position(line=0, character=3) assert codec.position_from_client_units( ['x="đź‹"'], types.Position(line=0, character=7) ) == types.Position(line=0, character=4) def test_position_to_utf16(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16) assert codec.position_to_client_units( ['x="đź‹"'], types.Position(line=0, character=3) ) == types.Position(line=0, character=3) assert codec.position_to_client_units( ['x="đź‹"'], types.Position(line=0, character=4) ) == types.Position(line=0, character=5) def test_position_to_utf32(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf32) assert codec.position_to_client_units( ['x="đź‹"'], types.Position(line=0, character=3) ) == types.Position(line=0, character=3) assert codec.position_to_client_units( ['x="đź‹"'], types.Position(line=0, character=4) ) == types.Position(line=0, character=4) def test_position_to_utf8(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf8) assert codec.position_to_client_units( ['x="đź‹"'], types.Position(line=0, character=3) ) == types.Position(line=0, character=3) assert codec.position_to_client_units( ['x="đź‹"'], types.Position(line=0, character=4) ) == types.Position(line=0, character=6) def test_range_from_utf16(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16) assert codec.range_from_client_units( ['x="đź‹"'], types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=5), ), ) == types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=4), ) range = types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=5), ) actual = codec.range_from_client_units(['x="đź‹đź‹"'], range) expected = types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=4), ) assert actual == expected def test_range_to_utf16(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16) assert codec.range_to_client_units( ['x="đź‹"'], types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=4), ), ) == types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=5), ) range = types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=4), ) actual = codec.range_to_client_units(['x="đź‹đź‹"'], range) expected = types.Range( start=types.Position(line=0, character=3), end=types.Position(line=0, character=5), ) assert actual == expected def test_offset_at_position_utf16(): doc = TextDocument(DOC_URI, DOC) assert doc.offset_at_position(types.Position(line=0, character=8)) == 8 assert doc.offset_at_position(types.Position(line=1, character=5)) == 12 assert doc.offset_at_position(types.Position(line=2, character=0)) == 13 assert doc.offset_at_position(types.Position(line=2, character=4)) == 17 assert doc.offset_at_position(types.Position(line=3, character=6)) == 27 assert doc.offset_at_position(types.Position(line=3, character=7)) == 28 assert doc.offset_at_position(types.Position(line=3, character=8)) == 28 assert doc.offset_at_position(types.Position(line=4, character=0)) == 40 assert doc.offset_at_position(types.Position(line=5, character=0)) == 40 def test_offset_at_position_utf32(): doc = TextDocument( DOC_URI, DOC, position_codec=PositionCodec(encoding=types.PositionEncodingKind.Utf32), ) assert doc.offset_at_position(types.Position(line=0, character=8)) == 8 assert doc.offset_at_position(types.Position(line=5, character=0)) == 39 def test_offset_at_position_utf8(): doc = TextDocument( DOC_URI, DOC, position_codec=PositionCodec(encoding=types.PositionEncodingKind.Utf8), ) assert doc.offset_at_position(types.Position(line=0, character=8)) == 8 assert doc.offset_at_position(types.Position(line=5, character=0)) == 41 def test_utf16_to_utf32_position_cast(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16) lines = ["", "đź‹đź‹", ""] assert codec.position_from_client_units( lines, types.Position(line=0, character=0) ) == types.Position(line=0, character=0) assert codec.position_from_client_units( lines, types.Position(line=0, character=1) ) == types.Position(line=0, character=0) assert codec.position_from_client_units( lines, types.Position(line=1, character=0) ) == types.Position(line=1, character=0) assert codec.position_from_client_units( lines, types.Position(line=1, character=2) ) == types.Position(line=1, character=1) assert codec.position_from_client_units( lines, types.Position(line=1, character=3) ) == types.Position(line=1, character=2) assert codec.position_from_client_units( lines, types.Position(line=1, character=4) ) == types.Position(line=1, character=2) assert codec.position_from_client_units( lines, types.Position(line=1, character=100) ) == types.Position(line=1, character=2) assert codec.position_from_client_units( lines, types.Position(line=3, character=0) ) == types.Position(line=2, character=0) assert codec.position_from_client_units( lines, types.Position(line=4, character=10) ) == types.Position(line=2, character=0) def test_position_for_line_endings(): codec = PositionCodec(encoding=types.PositionEncodingKind.Utf16) lines = ["x\r\n", "y\n"] assert codec.position_from_client_units( lines, types.Position(line=0, character=10) ) == types.Position(line=0, character=1) assert codec.position_from_client_units( lines, types.Position(line=1, character=10) ) == types.Position(line=1, character=1) def test_word_at_position(): """ Return word under the cursor (or last in line if past the end) """ doc = TextDocument(DOC_URI, DOC) assert doc.word_at_position(types.Position(line=0, character=8)) == "document" assert doc.word_at_position(types.Position(line=0, character=1000)) == "document" assert doc.word_at_position(types.Position(line=1, character=5)) == "for" assert doc.word_at_position(types.Position(line=2, character=0)) == "testing" assert doc.word_at_position(types.Position(line=3, character=10)) == "unicode" assert doc.word_at_position(types.Position(line=4, character=0)) == "" assert doc.word_at_position(types.Position(line=4, character=0)) == "" re_start_word = re.compile(r"[A-Za-z_0-9.]*$") re_end_word = re.compile(r"^[A-Za-z_0-9.]*") assert ( doc.word_at_position( types.Position( line=3, character=10, ), re_start_word=re_start_word, re_end_word=re_end_word, ) == "unicode." ) pygls-1.3.0/tests/test_feature_manager.py000066400000000000000000000612431455570166200205720ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import asyncio from typing import Any import pytest from pygls.capabilities import ServerCapabilitiesBuilder from pygls.exceptions import ( CommandAlreadyRegisteredError, FeatureAlreadyRegisteredError, ValidationError, ) from pygls.feature_manager import ( FeatureManager, has_ls_param_or_annotation, wrap_with_server, ) from lsprotocol import types as lsp class Temp: pass def test_has_ls_param_or_annotation(): def f1(ls, a, b, c): pass def f2(temp: Temp, a, b, c): pass def f3(temp: "Temp", a, b, c): pass assert has_ls_param_or_annotation(f1, None) assert has_ls_param_or_annotation(f2, Temp) assert has_ls_param_or_annotation(f3, Temp) def test_register_command_validation_error(feature_manager): with pytest.raises(ValidationError): @feature_manager.command(" \n\t") def cmd1(): # pylint: disable=unused-variable pass def test_register_commands(feature_manager): cmd1_name = "cmd1" cmd2_name = "cmd2" @feature_manager.command(cmd1_name) def cmd1(): pass @feature_manager.command(cmd2_name) def cmd2(): pass reg_commands = feature_manager.commands.keys() assert cmd1_name in reg_commands assert cmd2_name in reg_commands assert feature_manager.commands[cmd1_name] is cmd1 assert feature_manager.commands[cmd2_name] is cmd2 def test_register_feature_with_valid_options(feature_manager): options = lsp.CompletionOptions(trigger_characters=["!"]) @feature_manager.feature(lsp.TEXT_DOCUMENT_COMPLETION, options) def completions(): pass reg_features = feature_manager.features.keys() reg_feature_options = feature_manager.feature_options.keys() assert lsp.TEXT_DOCUMENT_COMPLETION in reg_features assert lsp.TEXT_DOCUMENT_COMPLETION in reg_feature_options assert feature_manager.features[lsp.TEXT_DOCUMENT_COMPLETION] is completions assert feature_manager.feature_options[lsp.TEXT_DOCUMENT_COMPLETION] is options def test_register_feature_with_wrong_options(feature_manager): class Options: pass with pytest.raises( AttributeError, match=("'Options' object has no attribute 'trigger_characters'"), # noqa ): @feature_manager.feature(lsp.TEXT_DOCUMENT_COMPLETION, Options()) def completions(): pass def test_register_features(feature_manager): @feature_manager.feature(lsp.TEXT_DOCUMENT_COMPLETION) def completions(): pass @feature_manager.feature(lsp.TEXT_DOCUMENT_CODE_LENS) def code_lens(): pass reg_features = feature_manager.features.keys() assert lsp.TEXT_DOCUMENT_COMPLETION in reg_features assert lsp.TEXT_DOCUMENT_CODE_LENS in reg_features assert feature_manager.features[lsp.TEXT_DOCUMENT_COMPLETION] is completions assert feature_manager.features[lsp.TEXT_DOCUMENT_CODE_LENS] is code_lens def test_register_same_command_twice_error(feature_manager): with pytest.raises(CommandAlreadyRegisteredError): @feature_manager.command("cmd1") def cmd1(): # pylint: disable=unused-variable pass @feature_manager.command("cmd1") def cmd2(): # pylint: disable=unused-variable pass def test_register_same_feature_twice_error(feature_manager): with pytest.raises(FeatureAlreadyRegisteredError): @feature_manager.feature(lsp.TEXT_DOCUMENT_CODE_ACTION) def code_action1(): # pylint: disable=unused-variable pass @feature_manager.feature(lsp.TEXT_DOCUMENT_CODE_ACTION) def code_action2(): # pylint: disable=unused-variable pass def test_wrap_with_server_async(): class Server: pass async def f(ls): assert isinstance(ls, Server) wrapped = wrap_with_server(f, Server()) assert asyncio.iscoroutinefunction(wrapped) def test_wrap_with_server_sync(): class Server: pass def f(ls): assert isinstance(ls, Server) wrapped = wrap_with_server(f, Server()) wrapped() def test_wrap_with_server_thread(): class Server: pass def f(ls): assert isinstance(ls, Server) f.execute_in_thread = True wrapped = wrap_with_server(f, Server()) assert wrapped.execute_in_thread is True def server_capabilities(**kwargs): """Helper to reduce the amount of boilerplate required to specify the expected server capabilities by filling in some fields - unless they are explicitly overriden.""" if "text_document_sync" not in kwargs: kwargs["text_document_sync"] = lsp.TextDocumentSyncOptions( open_close=False, save=False, ) if "execute_command_provider" not in kwargs: kwargs["execute_command_provider"] = lsp.ExecuteCommandOptions(commands=[]) if "workspace" not in kwargs: kwargs["workspace"] = lsp.ServerCapabilitiesWorkspaceType( workspace_folders=lsp.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True ), file_operations=lsp.FileOperationOptions(), ) if "position_encoding" not in kwargs: kwargs["position_encoding"] = lsp.PositionEncodingKind.Utf16 return lsp.ServerCapabilities(**kwargs) @pytest.mark.parametrize( "method, options, capabilities, expected", [ ( lsp.INITIALIZE, None, lsp.ClientCapabilities( general=lsp.GeneralClientCapabilities( position_encodings=[lsp.PositionEncodingKind.Utf8] ) ), server_capabilities(position_encoding=lsp.PositionEncodingKind.Utf8), ), ( lsp.INITIALIZE, None, lsp.ClientCapabilities( general=lsp.GeneralClientCapabilities( position_encodings=[ lsp.PositionEncodingKind.Utf8, lsp.PositionEncodingKind.Utf32, ] ) ), server_capabilities(position_encoding=lsp.PositionEncodingKind.Utf32), ), ( lsp.TEXT_DOCUMENT_DID_SAVE, lsp.SaveOptions(include_text=True), lsp.ClientCapabilities(), server_capabilities( text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=lsp.SaveOptions(include_text=True) ) ), ), ( lsp.TEXT_DOCUMENT_DID_SAVE, None, lsp.ClientCapabilities(), server_capabilities( text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=True ) ), ), ( lsp.TEXT_DOCUMENT_WILL_SAVE, None, lsp.ClientCapabilities(), server_capabilities( text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=False ) ), ), ( lsp.TEXT_DOCUMENT_WILL_SAVE, None, lsp.ClientCapabilities( text_document=lsp.TextDocumentClientCapabilities( synchronization=lsp.TextDocumentSyncClientCapabilities( will_save=True ) ) ), server_capabilities( text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=False, will_save=True ) ), ), ( lsp.TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, None, lsp.ClientCapabilities( text_document=lsp.TextDocumentClientCapabilities( synchronization=lsp.TextDocumentSyncClientCapabilities( will_save_wait_until=True ) ) ), server_capabilities( text_document_sync=lsp.TextDocumentSyncOptions( open_close=False, save=False, will_save_wait_until=True ) ), ), ( lsp.TEXT_DOCUMENT_DID_OPEN, None, lsp.ClientCapabilities(), server_capabilities( text_document_sync=lsp.TextDocumentSyncOptions( open_close=True, save=False ) ), ), ( lsp.TEXT_DOCUMENT_DID_CLOSE, None, lsp.ClientCapabilities(), server_capabilities( text_document_sync=lsp.TextDocumentSyncOptions( open_close=True, save=False ) ), ), ( lsp.TEXT_DOCUMENT_INLAY_HINT, None, lsp.ClientCapabilities(), server_capabilities( inlay_hint_provider=lsp.InlayHintOptions(resolve_provider=False), ), ), ( lsp.WORKSPACE_WILL_CREATE_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities(), server_capabilities(), ), ( lsp.WORKSPACE_WILL_CREATE_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( file_operations=lsp.FileOperationClientCapabilities( will_create=True ) ) ), server_capabilities( workspace=lsp.ServerCapabilitiesWorkspaceType( workspace_folders=lsp.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True ), file_operations=lsp.FileOperationOptions( will_create=lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ) ), ) ), ), ( lsp.WORKSPACE_DID_CREATE_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities(), server_capabilities(), ), ( lsp.WORKSPACE_DID_CREATE_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( file_operations=lsp.FileOperationClientCapabilities(did_create=True) ) ), server_capabilities( workspace=lsp.ServerCapabilitiesWorkspaceType( workspace_folders=lsp.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True ), file_operations=lsp.FileOperationOptions( did_create=lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ) ), ) ), ), ( lsp.WORKSPACE_WILL_DELETE_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities(), server_capabilities(), ), ( lsp.WORKSPACE_WILL_DELETE_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( file_operations=lsp.FileOperationClientCapabilities( will_delete=True ) ) ), server_capabilities( workspace=lsp.ServerCapabilitiesWorkspaceType( workspace_folders=lsp.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True ), file_operations=lsp.FileOperationOptions( will_delete=lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ) ), ) ), ), ( lsp.WORKSPACE_DID_DELETE_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities(), server_capabilities(), ), ( lsp.WORKSPACE_DID_DELETE_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( file_operations=lsp.FileOperationClientCapabilities(did_delete=True) ) ), server_capabilities( workspace=lsp.ServerCapabilitiesWorkspaceType( workspace_folders=lsp.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True ), file_operations=lsp.FileOperationOptions( did_delete=lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ) ), ) ), ), ( lsp.WORKSPACE_WILL_RENAME_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities(), server_capabilities(), ), ( lsp.WORKSPACE_WILL_RENAME_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( file_operations=lsp.FileOperationClientCapabilities( will_rename=True ) ) ), server_capabilities( workspace=lsp.ServerCapabilitiesWorkspaceType( workspace_folders=lsp.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True ), file_operations=lsp.FileOperationOptions( will_rename=lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ) ), ) ), ), ( lsp.WORKSPACE_DID_RENAME_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities(), server_capabilities(), ), ( lsp.WORKSPACE_DID_RENAME_FILES, lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ), lsp.ClientCapabilities( workspace=lsp.WorkspaceClientCapabilities( file_operations=lsp.FileOperationClientCapabilities(did_rename=True) ) ), server_capabilities( workspace=lsp.ServerCapabilitiesWorkspaceType( workspace_folders=lsp.WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True ), file_operations=lsp.FileOperationOptions( did_rename=lsp.FileOperationRegistrationOptions( filters=[ lsp.FileOperationFilter( pattern=lsp.FileOperationPattern(glob="**/*.py") ) ] ) ), ) ), ), ( lsp.WORKSPACE_SYMBOL, None, lsp.ClientCapabilities(), server_capabilities( workspace_symbol_provider=lsp.WorkspaceSymbolOptions( resolve_provider=False, ), ), ), ( lsp.TEXT_DOCUMENT_DIAGNOSTIC, None, lsp.ClientCapabilities(), server_capabilities( diagnostic_provider=lsp.DiagnosticOptions( inter_file_dependencies=False, workspace_diagnostics=False, ), ), ), ( lsp.TEXT_DOCUMENT_DIAGNOSTIC, lsp.DiagnosticOptions( workspace_diagnostics=True, inter_file_dependencies=True, ), lsp.ClientCapabilities(), server_capabilities( diagnostic_provider=lsp.DiagnosticOptions( inter_file_dependencies=True, workspace_diagnostics=False, ), ), ), ( lsp.TEXT_DOCUMENT_ON_TYPE_FORMATTING, None, lsp.ClientCapabilities(), server_capabilities( document_on_type_formatting_provider=None, ), ), ( lsp.TEXT_DOCUMENT_ON_TYPE_FORMATTING, lsp.DocumentOnTypeFormattingOptions(first_trigger_character=":"), lsp.ClientCapabilities(), server_capabilities( document_on_type_formatting_provider=lsp.DocumentOnTypeFormattingOptions( first_trigger_character=":", ), ), ), ], ) def test_register_feature( feature_manager: FeatureManager, method: str, options: Any, capabilities: lsp.ClientCapabilities, expected: lsp.ServerCapabilities, ): """Ensure that we can register features while specifying their associated options and that `pygls` is able to correctly build the corresponding server capabilities. Parameters ---------- feature_manager The feature manager to use method The method to register the feature handler for. options The method options to use capabilities The client capabilities to use when building the server's capabilities. expected The expected server capabilties we are expecting to see. """ @feature_manager.feature(method, options) def _(): pass actual = ServerCapabilitiesBuilder( capabilities, feature_manager.features.keys(), feature_manager.feature_options, [], None, None, ).build() assert expected == actual def test_register_inlay_hint_resolve(feature_manager: FeatureManager): @feature_manager.feature(lsp.TEXT_DOCUMENT_INLAY_HINT) def _(): pass @feature_manager.feature(lsp.INLAY_HINT_RESOLVE) def _(): pass expected = server_capabilities( inlay_hint_provider=lsp.InlayHintOptions(resolve_provider=True), ) actual = ServerCapabilitiesBuilder( lsp.ClientCapabilities(), feature_manager.features.keys(), feature_manager.feature_options, [], None, None, ).build() assert expected == actual def test_register_workspace_symbol_resolve(feature_manager: FeatureManager): @feature_manager.feature(lsp.WORKSPACE_SYMBOL) def _(): pass @feature_manager.feature(lsp.WORKSPACE_SYMBOL_RESOLVE) def _(): pass expected = server_capabilities( workspace_symbol_provider=lsp.WorkspaceSymbolOptions(resolve_provider=True), ) actual = ServerCapabilitiesBuilder( lsp.ClientCapabilities(), feature_manager.features.keys(), feature_manager.feature_options, [], None, None, ).build() assert expected == actual def test_register_workspace_diagnostics(feature_manager: FeatureManager): @feature_manager.feature( lsp.TEXT_DOCUMENT_DIAGNOSTIC, lsp.DiagnosticOptions( identifier="example", inter_file_dependencies=False, workspace_diagnostics=False, ), ) def _(): pass @feature_manager.feature(lsp.WORKSPACE_DIAGNOSTIC) def _(): pass expected = server_capabilities( diagnostic_provider=lsp.DiagnosticOptions( identifier="example", inter_file_dependencies=False, workspace_diagnostics=True, ), ) actual = ServerCapabilitiesBuilder( lsp.ClientCapabilities(), feature_manager.features.keys(), feature_manager.feature_options, [], None, None, ).build() assert expected == actual pygls-1.3.0/tests/test_language_server.py000066400000000000000000000110611455570166200206070ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import pathlib from time import sleep import pytest from pygls import IS_PYODIDE from lsprotocol.types import ( INITIALIZE, TEXT_DOCUMENT_DID_OPEN, WORKSPACE_EXECUTE_COMMAND, ) from lsprotocol.types import ( ClientCapabilities, DidOpenTextDocumentParams, ExecuteCommandParams, InitializeParams, TextDocumentItem, ) from pygls.protocol import LanguageServerProtocol from pygls.server import LanguageServer from . import CMD_ASYNC, CMD_SYNC, CMD_THREAD def _initialize_server(server): server.lsp.lsp_initialize( InitializeParams( process_id=1234, root_uri=pathlib.Path(__file__).parent.as_uri(), capabilities=ClientCapabilities(), ) ) def test_bf_initialize(client_server): client, server = client_server root_uri = pathlib.Path(__file__).parent.as_uri() process_id = 1234 response = client.lsp.send_request( INITIALIZE, InitializeParams( process_id=process_id, root_uri=root_uri, capabilities=ClientCapabilities(), ), ).result() assert server.process_id == process_id assert server.workspace.root_uri == root_uri assert response.capabilities is not None def test_bf_text_document_did_open(client_server): client, server = client_server _initialize_server(server) client.lsp.notify( TEXT_DOCUMENT_DID_OPEN, DidOpenTextDocumentParams( text_document=TextDocumentItem( uri=__file__, language_id="python", version=1, text="test" ) ), ) sleep(1) assert len(server.lsp.workspace.text_documents) == 1 document = server.workspace.get_text_document(__file__) assert document.uri == __file__ assert document.version == 1 assert document.source == "test" assert document.language_id == "python" @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") def test_command_async(client_server): client, server = client_server is_called, thread_id = client.lsp.send_request( WORKSPACE_EXECUTE_COMMAND, ExecuteCommandParams(command=CMD_ASYNC) ).result() assert is_called assert thread_id == server.thread_id @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") def test_command_sync(client_server): client, server = client_server is_called, thread_id = client.lsp.send_request( WORKSPACE_EXECUTE_COMMAND, ExecuteCommandParams(command=CMD_SYNC) ).result() assert is_called assert thread_id == server.thread_id @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") def test_command_thread(client_server): client, server = client_server is_called, thread_id = client.lsp.send_request( WORKSPACE_EXECUTE_COMMAND, ExecuteCommandParams(command=CMD_THREAD) ).result() assert is_called assert thread_id != server.thread_id def test_allow_custom_protocol_derived_from_lsp(): class CustomProtocol(LanguageServerProtocol): pass server = LanguageServer("pygls-test", "v1", protocol_cls=CustomProtocol) assert isinstance(server.lsp, CustomProtocol) def test_forbid_custom_protocol_not_derived_from_lsp(): class CustomProtocol: pass with pytest.raises(TypeError): LanguageServer("pygls-test", "v1", protocol_cls=CustomProtocol) pygls-1.3.0/tests/test_protocol.py000066400000000000000000000443001455570166200173010ustar00rootroot00000000000000############################################################################ # Copyright(c) Open Law Library. All rights reserved. # # See ThirdPartyNotices.txt in the project root for additional notices. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import io import json from concurrent.futures import Future from pathlib import Path from typing import Optional from unittest.mock import Mock import attrs import pytest from pygls.exceptions import JsonRpcException, JsonRpcInvalidParams from lsprotocol.types import ( PROGRESS, TEXT_DOCUMENT_COMPLETION, ClientCapabilities, CompletionItem, CompletionItemKind, CompletionParams, InitializeParams, InitializeResult, ProgressParams, Position, ShutdownResponse, TextDocumentCompletionResponse, TextDocumentIdentifier, WorkDoneProgressBegin, ) from pygls.protocol import ( default_converter, JsonRPCProtocol, JsonRPCRequestMessage, JsonRPCResponseMessage, JsonRPCNotification, ) EXAMPLE_NOTIFICATION = "example/notification" EXAMPLE_REQUEST = "example/request" @attrs.define class IntResult: id: str result: int jsonrpc: str = attrs.field(default="2.0") @attrs.define class ExampleParams: @attrs.define class InnerType: inner_field: str field_a: str field_b: Optional[InnerType] = None @attrs.define class ExampleNotification: jsonrpc: str = attrs.field(default="2.0") method: str = EXAMPLE_NOTIFICATION params: ExampleParams = attrs.field(default=None) @attrs.define class ExampleRequest: id: str jsonrpc: str = attrs.field(default="2.0") method: str = EXAMPLE_REQUEST params: ExampleParams = attrs.field(default=None) EXAMPLE_LSP_METHODS_MAP = { EXAMPLE_NOTIFICATION: (ExampleNotification, None, ExampleParams, None), EXAMPLE_REQUEST: (ExampleRequest, None, ExampleParams, None), } class ExampleProtocol(JsonRPCProtocol): def get_message_type(self, method: str): return EXAMPLE_LSP_METHODS_MAP.get(method, (None,))[0] @pytest.fixture() def protocol(): return ExampleProtocol(None, default_converter()) def test_deserialize_notification_message_valid_params(protocol): params = f""" {{ "jsonrpc": "2.0", "method": "{EXAMPLE_NOTIFICATION}", "params": {{ "fieldA": "test_a", "fieldB": {{ "innerField": "test_inner" }} }} }} """ result = json.loads(params, object_hook=protocol._deserialize_message) assert isinstance( result, ExampleNotification ), f"Expected FeatureRequest instance, got {result}" assert result.jsonrpc == "2.0" assert result.method == EXAMPLE_NOTIFICATION assert isinstance(result.params, ExampleParams) assert result.params.field_a == "test_a" assert isinstance(result.params.field_b, ExampleParams.InnerType) assert result.params.field_b.inner_field == "test_inner" def test_deserialize_notification_message_unknown_type(protocol): params = """ { "jsonrpc": "2.0", "method": "random", "params": { "field_a": "test_a", "field_b": { "inner_field": "test_inner" } } } """ result = json.loads(params, object_hook=protocol._deserialize_message) assert isinstance(result, JsonRPCNotification) assert result.jsonrpc == "2.0" assert result.method == "random" assert result.params.field_a == "test_a" assert result.params.field_b.inner_field == "test_inner" def test_deserialize_notification_message_bad_params_should_raise_error(protocol): params = f""" {{ "jsonrpc": "2.0", "method": "{EXAMPLE_NOTIFICATION}", "params": {{ "field_a": "test_a", "field_b": {{ "wrong_field_name": "test_inner" }} }} }} """ with pytest.raises(JsonRpcInvalidParams): json.loads(params, object_hook=protocol._deserialize_message) def test_deserialize_response_message_custom_converter(): params = """ { "jsonrpc": "2.0", "id": "id", "result": "1" } """ # Just for fun, let's create a converter that reverses all the keys in a dict. # @attrs.define class egasseM: cprnosj: str di: str tluser: str def structure_hook(obj, cls): params = {k[::-1]: v for k, v in obj.items()} return cls(**params) def custom_converter(): converter = default_converter() converter.register_structure_hook(egasseM, structure_hook) return converter protocol = JsonRPCProtocol(None, custom_converter()) protocol._result_types["id"] = egasseM result = json.loads(params, object_hook=protocol._deserialize_message) assert isinstance(result, egasseM) assert result.cprnosj == "2.0" assert result.di == "id" assert result.tluser == "1" @pytest.mark.parametrize( "method, params, expected", [ ( # Known notification type. PROGRESS, ProgressParams( token="id1", value=WorkDoneProgressBegin( title="Begin progress", percentage=0, ), ), { "jsonrpc": "2.0", "method": "$/progress", "params": { "token": "id1", "value": { "kind": "begin", "percentage": 0, "title": "Begin progress", }, }, }, ), ( # Custom notification type. EXAMPLE_NOTIFICATION, ExampleParams( field_a="field one", field_b=ExampleParams.InnerType(inner_field="field two"), ), { "jsonrpc": "2.0", "method": EXAMPLE_NOTIFICATION, "params": { "fieldA": "field one", "fieldB": { "innerField": "field two", }, }, }, ), ( # Custom notification with dict params. EXAMPLE_NOTIFICATION, {"fieldA": "field one", "fieldB": {"innerField": "field two"}}, { "jsonrpc": "2.0", "method": EXAMPLE_NOTIFICATION, "params": { "fieldA": "field one", "fieldB": { "innerField": "field two", }, }, }, ), ], ) def test_serialize_notification_message(method, params, expected): """ Ensure that we can serialize notification messages, retaining all expected fields. """ buffer = io.StringIO() protocol = JsonRPCProtocol(None, default_converter()) protocol._send_only_body = True protocol.connection_made(buffer) protocol.notify(method, params=params) actual = json.loads(buffer.getvalue()) assert actual == expected def test_deserialize_response_message(protocol): params = """ { "jsonrpc": "2.0", "id": "id", "result": "1" } """ protocol._result_types["id"] = IntResult result = json.loads(params, object_hook=protocol._deserialize_message) assert isinstance(result, IntResult) assert result.jsonrpc == "2.0" assert result.id == "id" assert result.result == 1 def test_deserialize_response_message_unknown_type(protocol): params = """ { "jsonrpc": "2.0", "id": "id", "result": { "field_a": "test_a", "field_b": { "inner_field": "test_inner" } } } """ protocol._result_types["id"] = JsonRPCResponseMessage result = json.loads(params, object_hook=protocol._deserialize_message) assert isinstance(result, JsonRPCResponseMessage) assert result.jsonrpc == "2.0" assert result.id == "id" assert result.result.field_a == "test_a" assert result.result.field_b.inner_field == "test_inner" def test_deserialize_request_message_with_registered_type(protocol): params = f""" {{ "jsonrpc": "2.0", "id": "id", "method": "{EXAMPLE_REQUEST}", "params": {{ "fieldA": "test_a", "fieldB": {{ "innerField": "test_inner" }} }} }} """ result = json.loads(params, object_hook=protocol._deserialize_message) assert isinstance(result, ExampleRequest) assert result.jsonrpc == "2.0" assert result.id == "id" assert result.method == EXAMPLE_REQUEST assert isinstance(result.params, ExampleParams) assert result.params.field_a == "test_a" assert isinstance(result.params.field_b, ExampleParams.InnerType) assert result.params.field_b.inner_field == "test_inner" def test_deserialize_request_message_without_registered_type(protocol): params = """ { "jsonrpc": "2.0", "id": "id", "method": "random", "params": { "field_a": "test_a", "field_b": { "inner_field": "test_inner" } } } """ result = json.loads(params, object_hook=protocol._deserialize_message) assert isinstance(result, JsonRPCRequestMessage) assert result.jsonrpc == "2.0" assert result.id == "id" assert result.method == "random" assert result.params.field_a == "test_a" assert result.params.field_b.inner_field == "test_inner" @pytest.mark.parametrize( "msg_type, result, expected", [ (ShutdownResponse, None, {"jsonrpc": "2.0", "id": "1", "result": None}), ( TextDocumentCompletionResponse, [ CompletionItem(label="example-one"), CompletionItem( label="example-two", kind=CompletionItemKind.Class, preselect=False, deprecated=True, ), ], { "jsonrpc": "2.0", "id": "1", "result": [ {"label": "example-one"}, { "label": "example-two", "kind": 7, # CompletionItemKind.Class "preselect": False, "deprecated": True, }, ], }, ), ( # Unknown type with object params. JsonRPCResponseMessage, ExampleParams( field_a="field one", field_b=ExampleParams.InnerType(inner_field="field two"), ), { "jsonrpc": "2.0", "id": "1", "result": { "fieldA": "field one", "fieldB": {"innerField": "field two"}, }, }, ), ( # Unknown type with dict params. JsonRPCResponseMessage, {"fieldA": "field one", "fieldB": {"innerField": "field two"}}, { "jsonrpc": "2.0", "id": "1", "result": { "fieldA": "field one", "fieldB": {"innerField": "field two"}, }, }, ), ], ) def test_serialize_response_message(msg_type, result, expected): """ Ensure that we can serialize response messages, retaining all expected fields. """ buffer = io.StringIO() protocol = JsonRPCProtocol(None, default_converter()) protocol._send_only_body = True protocol.connection_made(buffer) protocol._result_types["1"] = msg_type protocol._send_response("1", result=result) actual = json.loads(buffer.getvalue()) assert actual == expected @pytest.mark.parametrize( "method, params, expected", [ ( TEXT_DOCUMENT_COMPLETION, CompletionParams( text_document=TextDocumentIdentifier(uri="file:///file.txt"), position=Position(line=1, character=0), ), { "jsonrpc": "2.0", "id": "1", "method": TEXT_DOCUMENT_COMPLETION, "params": { "textDocument": {"uri": "file:///file.txt"}, "position": {"line": 1, "character": 0}, }, }, ), ( # Unknown type with object params. EXAMPLE_REQUEST, ExampleParams( field_a="field one", field_b=ExampleParams.InnerType(inner_field="field two"), ), { "jsonrpc": "2.0", "id": "1", "method": EXAMPLE_REQUEST, "params": { "fieldA": "field one", "fieldB": {"innerField": "field two"}, }, }, ), ( # Unknown type with dict params. EXAMPLE_REQUEST, {"fieldA": "field one", "fieldB": {"innerField": "field two"}}, { "jsonrpc": "2.0", "id": "1", "method": EXAMPLE_REQUEST, "params": { "fieldA": "field one", "fieldB": {"innerField": "field two"}, }, }, ), ], ) def test_serialize_request_message(method, params, expected): """ Ensure that we can serialize request messages, retaining all expected fields. """ buffer = io.StringIO() protocol = JsonRPCProtocol(None, default_converter()) protocol._send_only_body = True protocol.connection_made(buffer) protocol.send_request(method, params, callback=None, msg_id="1") actual = json.loads(buffer.getvalue()) assert actual == expected def test_data_received_without_content_type(client_server): _, server = client_server body = json.dumps( { "jsonrpc": "2.0", "method": "test", "params": 1, } ) message = "\r\n".join( ( "Content-Length: " + str(len(body)), "", body, ) ) data = bytes(message, "utf-8") server.lsp.data_received(data) def test_data_received_content_type_first_should_handle_message(client_server): _, server = client_server body = json.dumps( { "jsonrpc": "2.0", "method": "test", "params": 1, } ) message = "\r\n".join( ( "Content-Type: application/vscode-jsonrpc; charset=utf-8", "Content-Length: " + str(len(body)), "", body, ) ) data = bytes(message, "utf-8") server.lsp.data_received(data) def dummy_message(param=1): body = json.dumps( { "jsonrpc": "2.0", "method": "test", "params": param, } ) message = "\r\n".join( ( "Content-Length: " + str(len(body)), "Content-Type: application/vscode-jsonrpc; charset=utf-8", "", body, ) ) return bytes(message, "utf-8") def test_data_received_single_message_should_handle_message(client_server): _, server = client_server data = dummy_message() server.lsp.data_received(data) def test_data_received_partial_message_should_handle_message(client_server): _, server = client_server data = dummy_message() partial = len(data) - 5 server.lsp.data_received(data[:partial]) server.lsp.data_received(data[partial:]) def test_data_received_multi_message_should_handle_messages(client_server): _, server = client_server messages = (dummy_message(i) for i in range(3)) data = b"".join(messages) server.lsp.data_received(data) def test_data_received_error_should_raise_jsonrpc_error(client_server): _, server = client_server body = json.dumps( { "jsonrpc": "2.0", "id": "err", "error": { "code": -1, "message": "message for you sir", }, } ) message = "\r\n".join( [ "Content-Length: " + str(len(body)), "Content-Type: application/vscode-jsonrpc; charset=utf-8", "", body, ] ).encode("utf-8") future = server.lsp._request_futures["err"] = Future() server.lsp.data_received(message) with pytest.raises(JsonRpcException, match="message for you sir"): future.result() def test_initialize_should_return_server_capabilities(client_server): _, server = client_server params = InitializeParams( process_id=1234, root_uri=Path(__file__).parent.as_uri(), capabilities=ClientCapabilities(), ) server_capabilities = server.lsp.lsp_initialize(params) assert isinstance(server_capabilities, InitializeResult) def test_ignore_unknown_notification(client_server): _, server = client_server fn = server.lsp._execute_notification server.lsp._execute_notification = Mock() server.lsp._handle_notification("random/notification", None) assert not server.lsp._execute_notification.called # Remove mock server.lsp._execute_notification = fn pygls-1.3.0/tests/test_server_connection.py000066400000000000000000000071511455570166200211700ustar00rootroot00000000000000import asyncio import json import os from threading import Thread from unittest.mock import Mock import pytest from pygls import IS_PYODIDE from pygls.server import LanguageServer try: import websockets WEBSOCKETS_AVAILABLE = True except ImportError: WEBSOCKETS_AVAILABLE = False @pytest.mark.asyncio @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") async def test_tcp_connection_lost(): loop = asyncio.new_event_loop() server = LanguageServer("pygls-test", "v1", loop=loop) server.lsp.connection_made = Mock() server.lsp.connection_lost = Mock() # Run the server over TCP in a separate thread server_thread = Thread( target=server.start_tcp, args=( "127.0.0.1", 0, ), ) server_thread.daemon = True server_thread.start() # Wait for server to be ready while server._server is None: await asyncio.sleep(0.5) # Simulate client's connection port = server._server.sockets[0].getsockname()[1] reader, writer = await asyncio.open_connection("127.0.0.1", port) await asyncio.sleep(1) assert server.lsp.connection_made.called # Socket is closed (client's process is terminated) writer.close() await asyncio.sleep(1) assert server.lsp.connection_lost.called @pytest.mark.asyncio @pytest.mark.skipif(IS_PYODIDE, reason="threads are not available in pyodide.") async def test_io_connection_lost(): # Client to Server pipe. csr, csw = os.pipe() # Server to client pipe. scr, scw = os.pipe() server = LanguageServer("pygls-test", "v1", loop=asyncio.new_event_loop()) server.lsp.connection_made = Mock() server_thread = Thread( target=server.start_io, args=(os.fdopen(csr, "rb"), os.fdopen(scw, "wb")) ) server_thread.daemon = True server_thread.start() # Wait for server to be ready while not server.lsp.connection_made.called: await asyncio.sleep(0.5) # Pipe is closed (client's process is terminated) os.close(csw) server_thread.join() @pytest.mark.asyncio @pytest.mark.skipif( IS_PYODIDE or not WEBSOCKETS_AVAILABLE, reason="threads are not available in pyodide", ) async def test_ws_server(): """Smoke test to ensure we can send/receive messages over websockets""" loop = asyncio.new_event_loop() server = LanguageServer("pygls-test", "v1", loop=loop) # Run the server over Websockets in a separate thread server_thread = Thread( target=server.start_ws, args=( "127.0.0.1", 0, ), ) server_thread.daemon = True server_thread.start() # Wait for server to be ready while server._server is None: await asyncio.sleep(0.5) port = server._server.sockets[0].getsockname()[1] # Simulate client's connection async with websockets.connect(f"ws://127.0.0.1:{port}") as connection: # Send an 'initialize' request msg = dict( jsonrpc="2.0", id=1, method="initialize", params=dict(capabilities=dict()) ) await connection.send(json.dumps(msg)) response = await connection.recv() assert "result" in response # Shut the server down msg = dict( jsonrpc="2.0", id=2, method="shutdown", params=dict(capabilities=dict()) ) await connection.send(json.dumps(msg)) response = await connection.recv() assert "result" in response # Finally, tell it to exit msg = dict(jsonrpc="2.0", id=2, method="exit", params=None) await connection.send(json.dumps(msg)) server_thread.join() pygls-1.3.0/tests/test_types.py000066400000000000000000000072241455570166200166100ustar00rootroot00000000000000############################################################################ # Original work Copyright 2018 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ from lsprotocol.types import Location, Position, Range def test_position(): assert Position(line=1, character=2) == Position(line=1, character=2) assert Position(line=1, character=2) != Position(line=2, character=2) assert Position(line=1, character=2) <= Position(line=2, character=2) assert Position(line=2, character=2) >= Position(line=2, character=0) assert Position(line=1, character=2) != "something else" assert "1:2" == repr(Position(line=1, character=2)) def test_range(): assert Range( start=Position(line=1, character=2), end=Position(line=3, character=4) ) == Range(start=Position(line=1, character=2), end=Position(line=3, character=4)) assert Range( start=Position(line=0, character=2), end=Position(line=3, character=4) ) != Range(start=Position(line=1, character=2), end=Position(line=3, character=4)) assert ( Range(start=Position(line=0, character=2), end=Position(line=3, character=4)) != "something else" ) assert "1:2-3:4" == repr( Range(start=Position(line=1, character=2), end=Position(line=3, character=4)) ) def test_location(): assert Location( uri="file:///document.txt", range=Range( start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) == Location( uri="file:///document.txt", range=Range( start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) assert Location( uri="file:///document.txt", range=Range( start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) != Location( uri="file:///another.txt", range=Range( start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) assert ( Location( uri="file:///document.txt", range=Range( start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) != "something else" ) assert "file:///document.txt:1:2-3:4" == repr( Location( uri="file:///document.txt", range=Range( start=Position(line=1, character=2), end=Position(line=3, character=4) ), ) ) pygls-1.3.0/tests/test_uris.py000066400000000000000000000056271455570166200164330ustar00rootroot00000000000000############################################################################ # Original work Copyright 2018 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import pytest from pygls import uris from . import unix_only, windows_only @unix_only @pytest.mark.parametrize( "path,uri", [ ("/foo/bar", "file:///foo/bar"), ("/foo/space ?bar", "file:///foo/space%20%3Fbar"), ], ) def test_from_fs_path(path, uri): assert uris.from_fs_path(path) == uri @unix_only @pytest.mark.parametrize( "uri,path", [ ("file:///foo/bar#frag", "/foo/bar"), ("file:/foo/bar#frag", "/foo/bar"), ("file:/foo/space%20%3Fbar#frag", "/foo/space ?bar"), ], ) def test_to_fs_path(uri, path): assert uris.to_fs_path(uri) == path @pytest.mark.parametrize( "uri,kwargs,new_uri", [ ("file:///foo/bar", {"path": "/baz/boo"}, "file:///baz/boo"), ( "file:///D:/hello%20world.py", {"path": "D:/hello universe.py"}, "file:///d:/hello%20universe.py", ), ], ) def test_uri_with(uri, kwargs, new_uri): assert uris.uri_with(uri, **kwargs) == new_uri @windows_only @pytest.mark.parametrize( "path,uri", [ ("c:\\far\\boo", "file:///c:/far/boo"), ("C:\\far\\space ?boo", "file:///c:/far/space%20%3Fboo"), ], ) def test_win_from_fs_path(path, uri): assert uris.from_fs_path(path) == uri @windows_only @pytest.mark.parametrize( "uri,path", [ ("file:///c:/far/boo", "c:\\far\\boo"), ("file:///C:/far/boo", "c:\\far\\boo"), ("file:///C:/far/space%20%3Fboo", "c:\\far\\space ?boo"), ], ) def test_win_to_fs_path(uri, path): assert uris.to_fs_path(uri) == path pygls-1.3.0/tests/test_workspace.py000066400000000000000000000364161455570166200174470ustar00rootroot00000000000000############################################################################ # Original work Copyright 2017 Palantir Technologies, Inc. # # Original work licensed under the MIT License. # # See ThirdPartyNotices.txt in the project root for license information. # # All modifications Copyright (c) Open Law Library. All rights reserved. # # # # Licensed under the Apache License, Version 2.0 (the "License") # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http: // www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ import os import pytest from lsprotocol import types from pygls import uris from pygls.workspace import Workspace DOC_URI = uris.from_fs_path(__file__) DOC_TEXT = """test""" DOC = types.TextDocumentItem( uri=DOC_URI, language_id="plaintext", version=0, text=DOC_TEXT ) NOTEBOOK = types.NotebookDocument( uri="file:///path/to/notebook.ipynb", notebook_type="jupyter-notebook", version=0, cells=[ types.NotebookCell( kind=types.NotebookCellKind.Code, document="nb-cell-scheme://path/to/notebook.ipynb#cv32321", ), types.NotebookCell( kind=types.NotebookCellKind.Code, document="nb-cell-scheme://path/to/notebook.ipynb#cp897h32", ), ], ) NB_CELL_1 = types.TextDocumentItem( uri="nb-cell-scheme://path/to/notebook.ipynb#cv32321", language_id="python", version=0, text="# cell 1", ) NB_CELL_2 = types.TextDocumentItem( uri="nb-cell-scheme://path/to/notebook.ipynb#cp897h32", language_id="python", version=0, text="# cell 2", ) NB_CELL_3 = types.TextDocumentItem( uri="nb-cell-scheme://path/to/notebook.ipynb#cq343eeds", language_id="python", version=0, text="# cell 3", ) def test_add_folder(workspace): dir_uri = os.path.dirname(DOC_URI) dir_name = "test" workspace.add_folder(types.WorkspaceFolder(uri=dir_uri, name=dir_name)) assert workspace.folders[dir_uri].name == dir_name def test_get_notebook_document_by_uri(workspace): """Ensure that we can get a notebook given its uri.""" params = types.DidOpenNotebookDocumentParams( notebook_document=NOTEBOOK, cell_text_documents=[ NB_CELL_1, NB_CELL_2, ], ) workspace.put_notebook_document(params) notebook = workspace.get_notebook_document(notebook_uri=NOTEBOOK.uri) assert notebook == NOTEBOOK @pytest.mark.parametrize( "cell,expected", [ (NB_CELL_1, NOTEBOOK), (NB_CELL_2, NOTEBOOK), (NB_CELL_3, None), (DOC, None), ], ) def test_get_notebook_document_by_cell_uri(workspace, cell, expected): """Ensure that we can get a notebook given a uri of one of its cells""" params = types.DidOpenNotebookDocumentParams( notebook_document=NOTEBOOK, cell_text_documents=[ NB_CELL_1, NB_CELL_2, ], ) workspace.put_notebook_document(params) notebook = workspace.get_notebook_document(cell_uri=cell.uri) assert notebook == expected def test_get_text_document(workspace): workspace.put_text_document(DOC) assert workspace.get_text_document(DOC_URI).source == DOC_TEXT def test_get_missing_document(tmpdir, workspace): doc_path = tmpdir.join("test_document.py") doc_path.write(DOC_TEXT) doc_uri = uris.from_fs_path(str(doc_path)) assert workspace.get_text_document(doc_uri).source == DOC_TEXT def test_put_notebook_document(workspace): """Ensure that we can add notebook documents to the workspace correctly.""" params = types.DidOpenNotebookDocumentParams( notebook_document=NOTEBOOK, cell_text_documents=[ NB_CELL_1, NB_CELL_2, ], ) workspace.put_notebook_document(params) assert NOTEBOOK.uri in workspace._notebook_documents assert NB_CELL_1.uri in workspace._text_documents assert NB_CELL_2.uri in workspace._text_documents def test_put_text_document(workspace): workspace.put_text_document(DOC) assert DOC_URI in workspace._text_documents def test_remove_folder(workspace): dir_uri = os.path.dirname(DOC_URI) dir_name = "test" workspace.add_folder(types.WorkspaceFolder(uri=dir_uri, name=dir_name)) workspace.remove_folder(dir_uri) assert dir_uri not in workspace.folders def test_remove_notebook_document(workspace): """Ensure that we can correctly remove a document from the workspace.""" params = types.DidOpenNotebookDocumentParams( notebook_document=NOTEBOOK, cell_text_documents=[ NB_CELL_1, NB_CELL_2, ], ) workspace.put_notebook_document(params) assert NOTEBOOK.uri in workspace._notebook_documents assert NB_CELL_1.uri in workspace._text_documents assert NB_CELL_2.uri in workspace._text_documents params = types.DidCloseNotebookDocumentParams( notebook_document=types.NotebookDocumentIdentifier(uri=NOTEBOOK.uri), cell_text_documents=[ types.TextDocumentIdentifier(uri=NB_CELL_1.uri), types.TextDocumentIdentifier(uri=NB_CELL_2.uri), ], ) workspace.remove_notebook_document(params) assert NOTEBOOK.uri not in workspace._notebook_documents assert NB_CELL_1.uri not in workspace._text_documents assert NB_CELL_2.uri not in workspace._text_documents def test_remove_text_document(workspace): workspace.put_text_document(DOC) assert workspace.get_text_document(DOC_URI).source == DOC_TEXT workspace.remove_text_document(DOC_URI) assert workspace.get_text_document(DOC_URI)._source is None def test_update_notebook_metadata(workspace): """Ensure we can update a notebook's metadata correctly.""" params = types.DidOpenNotebookDocumentParams( notebook_document=NOTEBOOK, cell_text_documents=[ NB_CELL_1, NB_CELL_2, ], ) workspace.put_notebook_document(params) notebook = workspace.get_notebook_document(notebook_uri=NOTEBOOK.uri) assert notebook.version == 0 assert notebook.metadata is None params = types.DidChangeNotebookDocumentParams( notebook_document=types.VersionedNotebookDocumentIdentifier( uri=NOTEBOOK.uri, version=31 ), change=types.NotebookDocumentChangeEvent( metadata={"custom": "metadata"}, ), ) workspace.update_notebook_document(params) notebook = workspace.get_notebook_document(notebook_uri=NOTEBOOK.uri) assert notebook.version == 31 assert notebook.metadata == {"custom": "metadata"} def test_update_notebook_cell_data(workspace): """Ensure we can update a notebook correctly when cell data changes.""" params = types.DidOpenNotebookDocumentParams( notebook_document=NOTEBOOK, cell_text_documents=[ NB_CELL_1, NB_CELL_2, ], ) workspace.put_notebook_document(params) notebook = workspace.get_notebook_document(notebook_uri=NOTEBOOK.uri) assert notebook.version == 0 cell_1 = notebook.cells[0] assert cell_1.metadata is None assert cell_1.execution_summary is None cell_2 = notebook.cells[1] assert cell_2.metadata is None assert cell_2.execution_summary is None params = types.DidChangeNotebookDocumentParams( notebook_document=types.VersionedNotebookDocumentIdentifier( uri=NOTEBOOK.uri, version=31 ), change=types.NotebookDocumentChangeEvent( cells=types.NotebookDocumentChangeEventCellsType( data=[ types.NotebookCell( kind=types.NotebookCellKind.Code, document=NB_CELL_1.uri, metadata={"slideshow": {"slide_type": "skip"}}, execution_summary=types.ExecutionSummary( execution_order=2, success=True ), ), types.NotebookCell( kind=types.NotebookCellKind.Code, document=NB_CELL_2.uri, metadata={"slideshow": {"slide_type": "note"}}, execution_summary=types.ExecutionSummary( execution_order=3, success=False ), ), ] ) ), ) workspace.update_notebook_document(params) notebook = workspace.get_notebook_document(notebook_uri=NOTEBOOK.uri) assert notebook.version == 31 cell_1 = notebook.cells[0] assert cell_1.metadata == {"slideshow": {"slide_type": "skip"}} assert cell_1.execution_summary == types.ExecutionSummary( execution_order=2, success=True ) cell_2 = notebook.cells[1] assert cell_2.metadata == {"slideshow": {"slide_type": "note"}} assert cell_2.execution_summary == types.ExecutionSummary( execution_order=3, success=False ) def test_update_notebook_cell_content(workspace): """Ensure we can update a notebook correctly when the cell contents change.""" params = types.DidOpenNotebookDocumentParams( notebook_document=NOTEBOOK, cell_text_documents=[ NB_CELL_1, NB_CELL_2, ], ) workspace.put_notebook_document(params) notebook = workspace.get_notebook_document(notebook_uri=NOTEBOOK.uri) assert notebook.version == 0 cell_1 = workspace.get_text_document(NB_CELL_1.uri) assert cell_1.version == 0 assert cell_1.source == "# cell 1" cell_2 = workspace.get_text_document(NB_CELL_2.uri) assert cell_2.version == 0 assert cell_2.source == "# cell 2" params = types.DidChangeNotebookDocumentParams( notebook_document=types.VersionedNotebookDocumentIdentifier( uri=NOTEBOOK.uri, version=31 ), change=types.NotebookDocumentChangeEvent( cells=types.NotebookDocumentChangeEventCellsType( text_content=[ types.NotebookDocumentChangeEventCellsTypeTextContentType( document=types.VersionedTextDocumentIdentifier( uri=NB_CELL_1.uri, version=13 ), changes=[ types.TextDocumentContentChangeEvent_Type1( text="new text", range=types.Range( start=types.Position(line=0, character=0), end=types.Position(line=0, character=8), ), ) ], ), types.NotebookDocumentChangeEventCellsTypeTextContentType( document=types.VersionedTextDocumentIdentifier( uri=NB_CELL_2.uri, version=21 ), changes=[ types.TextDocumentContentChangeEvent_Type1( text="", range=types.Range( start=types.Position(line=0, character=0), end=types.Position(line=0, character=8), ), ), types.TextDocumentContentChangeEvent_Type1( text="other text", range=types.Range( start=types.Position(line=0, character=0), end=types.Position(line=0, character=0), ), ), ], ), ] ) ), ) workspace.update_notebook_document(params) notebook = workspace.get_notebook_document(notebook_uri=NOTEBOOK.uri) assert notebook.version == 31 cell_1 = workspace.get_text_document(NB_CELL_1.uri) assert cell_1.version == 13 assert cell_1.source == "new text" cell_2 = workspace.get_text_document(NB_CELL_2.uri) assert cell_2.version == 21 assert cell_2.source == "other text" def test_update_notebook_new_cells(workspace): """Ensure that we can correctly add new cells to an existing notebook.""" params = types.DidOpenNotebookDocumentParams( notebook_document=NOTEBOOK, cell_text_documents=[ NB_CELL_1, NB_CELL_2, ], ) workspace.put_notebook_document(params) notebook = workspace.get_notebook_document(notebook_uri=NOTEBOOK.uri) assert notebook.version == 0 cell_uris = [c.document for c in notebook.cells] assert cell_uris == [NB_CELL_1.uri, NB_CELL_2.uri] cell_1 = workspace.get_text_document(NB_CELL_1.uri) assert cell_1.version == 0 assert cell_1.source == "# cell 1" cell_2 = workspace.get_text_document(NB_CELL_2.uri) assert cell_2.version == 0 assert cell_2.source == "# cell 2" params = types.DidChangeNotebookDocumentParams( notebook_document=types.VersionedNotebookDocumentIdentifier( uri=NOTEBOOK.uri, version=31 ), change=types.NotebookDocumentChangeEvent( cells=types.NotebookDocumentChangeEventCellsType( structure=types.NotebookDocumentChangeEventCellsTypeStructureType( array=types.NotebookCellArrayChange( start=1, delete_count=0, cells=[ types.NotebookCell( kind=types.NotebookCellKind.Code, document=NB_CELL_3.uri ) ], ), did_open=[NB_CELL_3], ) ) ), ) workspace.update_notebook_document(params) notebook = workspace.get_notebook_document(cell_uri=NB_CELL_3.uri) assert notebook.uri == NOTEBOOK.uri assert NB_CELL_3.uri in workspace._text_documents cell_uris = [c.document for c in notebook.cells] assert cell_uris == [NB_CELL_1.uri, NB_CELL_3.uri, NB_CELL_2.uri] def test_workspace_folders(): wf1 = types.WorkspaceFolder(uri="/ws/f1", name="ws1") wf2 = types.WorkspaceFolder(uri="/ws/f2", name="ws2") workspace = Workspace("/ws", workspace_folders=[wf1, wf2]) assert workspace.folders["/ws/f1"] is wf1 assert workspace.folders["/ws/f2"] is wf2 def test_null_workspace(): workspace = Workspace(None) assert workspace.root_uri is None assert workspace.root_path is None