40 Commits

Author SHA1 Message Date
Charlie Vieth
981150e308 poetry: update pygls to version 0.12 (#55)
Update pygls to version 0.12 to remove a conflict with
jedi-language-server which requires pygls "^0.12" and set the minimum
python version to py37 (as required by pygls 0.12).

This commit also bumps the cmake-language-server version to 0.1.36
2022-08-04 22:28:14 +09:00
Regen
6de2cc3867 Release v0.1.5 (#53) 2022-06-11 12:09:33 +09:00
Regen
1fefcb4cba Use cmakelang formatter (#52) 2022-05-01 23:43:22 +09:00
Regen
f0cfa8b13f Release v0.1.4 (#51) 2022-04-30 23:54:34 +09:00
Regen
722dae419e Remove cmake-format (#50) 2022-04-30 02:54:19 +09:00
Regen
6a3140f105 Fix CI (#49) 2022-04-30 02:28:35 +09:00
dev-bz
4dec2f5afa Update server.py (#48)
Fixed a spelling error
2022-04-18 12:20:58 +09:00
offa
9f7754f0e6 Add Python 3.10 CI build (#45) 2021-10-07 12:28:22 +09:00
Charlie Vieth
9630b96935 poetry: update pygls to version 0.11.2 (#42)
Update pygls to version 0.11.2 and change the version requirement to
'^0.11' this removes a conflict with jedi-language-server which requires
pygls>=0.11.1,<0.12.0.

This commit also bumps the cmake-language-server version to 0.1.3.
2021-10-03 15:13:39 +09:00
Regen
76e34ae628 Fix __version__ (#43) 2021-10-03 12:51:16 +09:00
Regen
cbb6bdd1ae Fix parse_modules (#44) 2021-10-03 12:46:02 +09:00
Regen
a5af5b505f Update pygls (#38) 2021-03-28 23:49:27 +09:00
Regen
4d120a6a98 Replace linter (#37) 2021-03-28 22:23:57 +09:00
KOLANICH
6e839f7675 Added .editorconfig. (#35)
Applies some elements of PEP8 during editing in the editors supporting it.
2021-01-23 16:07:22 +09:00
KOLANICH
cade1e2c45 Fixed build system requirements in pyproject.toml. (#34)
Poetry depends on lot of dependencies having nothing common with building packages. That's why poetry-core was created, which has much less dependencies and more focused on building wheels.
2021-01-23 16:04:06 +09:00
Regen
d16d3b24ef Merge pull request #31 from jargonzombies/doc-and-spelling
Some documentation and misspelling fixes
2020-11-08 16:48:02 +09:00
Jan-Grimo Sobez
4be7657edb Some documentation and misspelling fixes 2020-11-07 22:09:42 +01:00
Regen
040f0b9f0c Merge pull request #28 from regen100/add-doc
Add doc
2020-08-22 16:58:58 +09:00
Regen
ef2c31c6a3 Add doc 2020-08-22 15:15:45 +09:00
Regen
87879ee5df Merge pull request #27 from regen100/replace-linter
Replace yapf with black
2020-08-22 14:54:15 +09:00
Regen
01b1fac73e Replace yapf with black 2020-08-22 14:38:29 +09:00
Regen
5550cb259c Merge pull request #24 from r-burns/darwin
Fix test_read_cmake_files on macOS
2020-07-29 12:42:32 +09:00
Regen
466c5b7bcc Run CI on PR 2020-07-29 12:31:44 +09:00
Ryan Burns
0ec120f391 Fix test_read_cmake_files on macOS 2020-07-27 00:55:16 -07:00
Regen
5d916b6989 Merge pull request #19 from regen100/release-v0.1.2
Release v0.1.2
2020-06-07 22:01:20 +09:00
Regen
1c606ee8a8 Release v0.1.2 2020-06-07 21:50:21 +09:00
Regen
cdb62adce3 Merge pull request #18 from regen100/fix-ci
Fix CI
2020-06-07 20:17:27 +09:00
Regen
6ac3b1d17f Fix CI 2020-06-07 19:52:24 +09:00
Regen
ce2c3a21db Merge pull request #17 from regen100/update-doc
Update README
2020-06-06 15:23:44 +09:00
Regen
3697fae2d3 Update README 2020-06-06 14:52:03 +09:00
Regen
48d5980a36 Merge pull request #13 from regen100/fix-vim-lsp
Fix vim lsp
2020-04-29 23:45:46 +09:00
Regen
e07b3242c8 Add test 2020-04-29 22:57:43 +09:00
Regen
2d36887b26 Support no TriggerCharacter 2020-04-29 19:25:51 +09:00
Regen
67aced6544 Add encoding 2020-04-29 18:04:23 +09:00
Regen
3c171b9e25 Fix CompletionItem for vim-lsp 2020-04-29 17:56:09 +09:00
Regen
c8c284e061 Merge pull request #12 from regen100/fix-completion
Fix error without completion context
2020-04-29 16:09:21 +09:00
Regen
6bf08e0f14 Fix error without completion context 2020-04-29 15:57:25 +09:00
Regen
40d93525d9 Merge pull request #8 from regen100/add-version
Add --version to cmake-language-server
2020-03-07 00:26:36 +09:00
Regen
f8136d6dbc Disable patch status 2020-03-07 00:21:50 +09:00
Regen
5af6555d3c Add --version to cmake-language-server 2020-03-07 00:09:43 +09:00
21 changed files with 1023 additions and 1235 deletions

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
root = 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

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
poetry.lock linguist-generated=true

View File

@@ -1,18 +1,19 @@
name: Tests name: Tests
on: [push] on: [pull_request]
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false
matrix: matrix:
python: [3.6, 3.7, 3.8] python: ["3.7", "3.8", "3.9", "3.10"]
os: [ubuntu-18.04, windows-2016] os: [ubuntu-18.04, windows-2019]
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python }} - name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v1 uses: actions/setup-python@v2
with: with:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
- name: Setup VC - name: Setup VC
@@ -21,21 +22,24 @@ jobs:
- name: Install CMake - name: Install CMake
if: contains(matrix.os, 'ubuntu') if: contains(matrix.os, 'ubuntu')
run: | run: |
CMAKE_VERSION=3.14.7 CMAKE_VERSION=3.17.3
curl -sSL https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz | tar xz curl -sSL https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz | tar xz
sudo cp -rT cmake-$CMAKE_VERSION-Linux-x86_64 /usr/local sudo cp -rT cmake-$CMAKE_VERSION-Linux-x86_64 /usr/local
rm -rf cmake-$CMAKE_VERSION-Linux-x86_64 rm -rf cmake-$CMAKE_VERSION-Linux-x86_64
- name: Install dependencies - name: Install dependencies
run: | run: |
cmake --version
python -m pip install --upgrade setuptools pip wheel python -m pip install --upgrade setuptools pip wheel
python -m pip install poetry tox-gh-actions python -m pip install poetry tox-gh-actions
- name: Test with tox - name: Test with tox
run: | run: |
tox tox
env:
os: ${{ matrix.os }}
- name: Upload coverage reports to Codecov - name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v1 uses: codecov/codecov-action@v1
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests flags: unittests
name: Python ${{ matrix.python }} on ${{ matrix.os }} name: Python ${{ matrix.python }} on ${{ matrix.os }}
fail_ci_if_error: true fail_ci_if_error: false

129
.gitignore vendored
View File

@@ -1,133 +1,4 @@
### https://raw.github.com/github/gitignore/cb0c6ef7ac68f2300409ee85501d9ad432cb4c7e/Python.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-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/ .tox/
.nox/
.coverage .coverage
.coverage.*
.cache
nosetests.xml
coverage.xml coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# 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
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

View File

@@ -1,2 +0,0 @@
[style]
based_on_style = pep8

View File

@@ -12,12 +12,11 @@ Alpha Stage, work in progress.
## Features ## Features
- [x] Builtin command completion - [x] Builtin command completion
- [x] Documentation for commands and variables on hover - [x] Documentation for commands and variables on hover
- [x] Formatting - [x] Formatting (by [`cmake-format`](https://github.com/cheshirekow/cmake_format))
## Commands ## Commands
- `cmake-language-server`: LSP server - `cmake-language-server`: LSP server
- `cmake-format`: CLI frontend for formatting
## Installation ## Installation
@@ -25,12 +24,14 @@ Alpha Stage, work in progress.
$ pip install cmake-language-server $ pip install cmake-language-server
``` ```
### Clients ### Tested Clients
- Neovim ([neoclide/coc.nvim][coc.nvim]) - Neovim ([neoclide/coc.nvim][coc.nvim], [prabirshrestha/vim-lsp][vim-lsp])
#### Neovim #### Neovim
##### coc.nvim
```jsonc ```jsonc
"languageserver": { "languageserver": {
"cmake": { "cmake": {
@@ -46,5 +47,29 @@ $ pip install cmake-language-server
} }
``` ```
##### vim-lsp
```vim
if executable('cmake-language-server')
au User lsp_setup call lsp#register_server({
\ 'name': 'cmake',
\ 'cmd': {server_info->['cmake-language-server']},
\ 'root_uri': {server_info->lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'build/'))},
\ 'whitelist': ['cmake'],
\ 'initialization_options': {
\ 'buildDirectory': 'build',
\ }
\})
endif
```
### Configuration
* `buildDirectory`
This language server uses CMake's file API to get cached variables.
The API communicates using `<buildDirectory>/.cmake/api/`.
`buildDirectory` is relative path to the root uri of the workspace.
To configure the build tree, you need to run the cmake command such as `cmake .. -DFOO=bar`.
[coc.nvim]: https://github.com/neoclide/coc.nvim [coc.nvim]: https://github.com/neoclide/coc.nvim
[vim-lsp]: https://github.com/prabirshrestha/vim-lsp

View File

@@ -4,5 +4,4 @@ coverage:
default: default:
threshold: 10% threshold: 10%
patch: patch:
default: default: off
threshold: 20%

View File

@@ -1,6 +0,0 @@
[mypy]
ignore_missing_imports = True
allow_redefinition = True
[mypy-re.Scanner]
ignore_errors = True

594
poetry.lock generated
View File

@@ -1,368 +1,424 @@
[[package]] [[package]]
category = "dev"
description = "Atomic file writes."
name = "atomicwrites" name = "atomicwrites"
version = "1.4.0"
description = "Atomic file writes."
category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.3.0"
[[package]] [[package]]
category = "dev"
description = "Classes Without Boilerplate"
name = "attrs" name = "attrs"
version = "21.4.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "19.3.0"
[package.extras]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
[[package]] [[package]]
name = "cmakelang"
version = "0.6.13"
description = "Language tools for cmake (format, lint, etc)"
category = "dev" category = "dev"
description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.4.1"
[[package]]
category = "dev"
description = "Code coverage measurement for Python"
name = "coverage"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
version = "5.0"
[[package]]
category = "dev"
description = "Discover and load entry points from installed packages."
name = "entrypoints"
optional = false
python-versions = ">=2.7"
version = "0.3"
[[package]]
category = "dev"
description = "A platform independent file lock."
name = "filelock"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "3.0.12"
[[package]]
category = "dev"
description = "the modular source code checker: pep8, pyflakes and co"
name = "flake8"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.7.8"
[package.dependencies] [package.dependencies]
entrypoints = ">=0.3.0,<0.4.0" six = ">=1.13.0"
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.5.0,<2.6.0" [package.extras]
pyflakes = ">=2.1.0,<2.2.0" yaml = ["pyyaml (>=5.3)"]
html-gen = ["jinja2 (==2.10.3)"]
[[package]] [[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "dev" category = "dev"
description = "Read metadata from Python packages" optional = false
marker = "python_version < \"3.8\"" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "coverage"
version = "6.2"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
toml = ["tomli"]
[[package]]
name = "importlib-metadata" name = "importlib-metadata"
version = "4.8.3"
description = "Read metadata from Python packages"
category = "main"
optional = false optional = false
python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" python-versions = ">=3.6"
version = "0.23"
[package.dependencies] [package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5" zipp = ">=0.5"
[[package]] [package.extras]
category = "dev" docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
description = "A Python utility / library to sort Python imports." perf = ["ipython"]
name = "isort" testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "4.3.21"
[[package]] [[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev" category = "dev"
description = "McCabe checker, plugin for flake8"
name = "mccabe"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "0.6.1"
[[package]] [[package]]
category = "dev"
description = "More routines for operating on iterables, beyond itertools"
name = "more-itertools"
optional = false
python-versions = ">=3.4"
version = "7.2.0"
[[package]]
category = "dev"
description = "Optional static typing for Python"
name = "mypy"
optional = false
python-versions = ">=3.5"
version = "0.740"
[package.dependencies]
mypy-extensions = ">=0.4.0,<0.5.0"
typed-ast = ">=1.4.0,<1.5.0"
typing-extensions = ">=3.7.4"
[[package]]
category = "dev"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
name = "mypy-extensions"
optional = false
python-versions = "*"
version = "0.4.3"
[[package]]
category = "dev"
description = "Core utilities for Python packages"
name = "packaging" name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=3.6"
version = "19.2"
[package.dependencies] [package.dependencies]
pyparsing = ">=2.0.2" pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
six = "*"
[[package]] [[package]]
category = "dev"
description = "plugin and hook calling mechanisms for python"
name = "pluggy" name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=3.6"
version = "0.13.0"
[package.dependencies] [package.dependencies]
[package.dependencies.importlib-metadata] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
python = "<3.8"
version = ">=0.12" [package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]] [[package]]
category = "dev"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
name = "py" name = "py"
optional = false version = "1.11.0"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" description = "library with cross-python path, ini-parsing, io, code, log facilities"
version = "1.8.0"
[[package]]
category = "dev" category = "dev"
description = "Python style guide checker"
name = "pycodestyle"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.5.0"
[[package]]
category = "dev"
description = "passive checker of Python programs"
name = "pyflakes"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.1.1"
[[package]] [[package]]
name = "pydantic"
version = "1.9.1"
description = "Data validation and settings management using python type hints"
category = "main" category = "main"
description = "a pythonic generic language server (pronounced like \"pie glass\")." optional = false
python-versions = ">=3.6.1"
[package.dependencies]
typing-extensions = ">=3.7.4.3"
[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
[[package]]
name = "pygls" name = "pygls"
optional = false version = "0.12"
python-versions = "*" description = "a pythonic generic language server (pronounced like \"pie glass\")."
version = "0.8.1"
[[package]]
category = "main" category = "main"
description = "Python parsing module" optional = false
python-versions = "<3.11,>=3.7"
[package.dependencies]
pydantic = ">=1.9.1,<1.10"
typeguard = ">=2.10.0,<3"
[package.extras]
dev = ["bandit (==1.7.4)", "flake8 (==4.0.1)", "mypy (==0.961)"]
docs = ["sphinx (==5.0.1)", "sphinx-rtd-theme (==1.0.0)"]
test = ["mock (==4.0.3)", "pytest (==7.1.2)", "pytest-asyncio (==0.18.3)"]
ws = ["websockets (>=10.0.0,<11.0.0)"]
[[package]]
name = "pyparsing" name = "pyparsing"
version = "3.0.7"
description = "Python parsing module"
category = "dev"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=3.6"
version = "2.4.2"
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]] [[package]]
category = "dev"
description = "pytest: simple powerful testing with Python"
name = "pytest" name = "pytest"
version = "6.2.5"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.6"
version = "5.2.1"
[package.dependencies] [package.dependencies]
atomicwrites = ">=1.0" atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=17.4.0" attrs = ">=19.2.0"
colorama = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""}
more-itertools = ">=4.0.0" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
iniconfig = "*"
packaging = "*" packaging = "*"
pluggy = ">=0.12,<1.0" pluggy = ">=0.12,<2.0"
py = ">=1.5.0" py = ">=1.8.2"
wcwidth = "*" toml = "*"
[package.dependencies.importlib-metadata] [package.extras]
python = "<3.8" testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
version = ">=0.12"
[[package]] [[package]]
category = "dev"
description = "Pytest plugin for measuring coverage."
name = "pytest-cov" name = "pytest-cov"
version = "2.12.1"
description = "Pytest plugin for measuring coverage."
category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.8.1"
[package.dependencies] [package.dependencies]
coverage = ">=4.4" coverage = ">=5.2.1"
pytest = ">=3.6" pytest = ">=4.6"
toml = "*"
[package.extras]
testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
[[package]] [[package]]
category = "dev"
description = "pytest plugin for test data directories and files"
name = "pytest-datadir" name = "pytest-datadir"
version = "1.3.1"
description = "pytest plugin for test data directories and files"
category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.3.1"
[package.dependencies] [package.dependencies]
pytest = ">=2.7.0" pytest = ">=2.7.0"
[[package]] [[package]]
category = "dev"
description = "Python 2 and 3 compatibility utilities"
name = "six" name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "dev"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
version = "1.12.0"
[[package]] [[package]]
category = "dev"
description = "Python Library for Tom's Obvious, Minimal Language"
name = "toml" name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
category = "dev"
optional = false optional = false
python-versions = "*" python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
version = "0.10.0"
[[package]] [[package]]
category = "dev" name = "typeguard"
description = "tox is a generic virtualenv management and test command line tool" version = "2.13.3"
name = "tox" description = "Run-time type checker for Python"
category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=3.5.3"
version = "3.14.0"
[package.dependencies] [package.extras]
filelock = ">=3.0.0,<4" doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
packaging = ">=14" test = ["pytest", "typing-extensions", "mypy"]
pluggy = ">=0.12.0,<1"
py = ">=1.4.17,<2"
six = ">=1.0.0,<2"
toml = ">=0.9.4"
virtualenv = ">=14.0.0"
[package.dependencies.importlib-metadata]
python = "<3.8"
version = ">=0.12,<1"
[[package]] [[package]]
category = "dev"
description = "a fork of Python 2 and 3 ast modules with type comment support"
name = "typed-ast"
optional = false
python-versions = "*"
version = "1.4.0"
[[package]]
category = "dev"
description = "Type Hints for Python"
name = "typing"
optional = false
python-versions = "*"
version = "3.7.4.1"
[[package]]
category = "dev"
description = "Backported and Experimental Type Hints for Python 3.5+"
name = "typing-extensions" name = "typing-extensions"
version = "4.1.1"
description = "Backported and Experimental Type Hints for Python 3.6+"
category = "main"
optional = false optional = false
python-versions = "*" python-versions = ">=3.6"
version = "3.7.4"
[package.dependencies]
typing = ">=3.7.4"
[[package]] [[package]]
category = "dev"
description = "Virtual Python Environment builder"
name = "virtualenv"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
version = "16.7.7"
[[package]]
category = "dev"
description = "Measures number of Terminal column cells of wide-character codes"
name = "wcwidth"
optional = false
python-versions = "*"
version = "0.1.7"
[[package]]
category = "dev"
description = "A formatter for Python code."
name = "yapf"
optional = false
python-versions = "*"
version = "0.28.0"
[[package]]
category = "dev"
description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version < \"3.8\""
name = "zipp" name = "zipp"
version = "3.6.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false optional = false
python-versions = ">=2.7" python-versions = ">=3.6"
version = "0.6.0"
[package.dependencies] [package.extras]
more-itertools = "*" docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
[metadata] [metadata]
content-hash = "284b539e6199a16441b6196fcbc38a374c886e328ae0c5e8bf07d0aaa47b0670" lock-version = "1.1"
python-versions = "^3.6" python-versions = "^3.7,<3.11"
content-hash = "611ecc79e7246a5e3136e80b2b5937ee5a097866faaa2ad251e5143985c267a9"
[metadata.hashes] [metadata.files]
atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"] atomicwrites = [
attrs = ["08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"] {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
coverage = ["0cd13a6e98c37b510a2d34c8281d5e1a226aaf9b65b7d770ef03c63169965351", "1a4b6b6a2a3a6612e6361130c2cc3dc4378d8c221752b96167ccbad94b47f3cd", "2ee55e6dba516ddf6f484aa83ccabbb0adf45a18892204c23486938d12258cde", "3be5338a2eb4ef03c57f20917e1d12a1fd10e3853fed060b6d6b677cb3745898", "44b783b02db03c4777d8cf71bae19eadc171a6f2a96777d916b2c30a1eb3d070", "475bf7c4252af0a56e1abba9606f1e54127cdf122063095c75ab04f6f99cf45e", "47c81ee687eafc2f1db7f03fbe99aab81330565ebc62fb3b61edfc2216a550c8", "4a7f8e72b18f2aca288ff02255ce32cc830bc04d993efbc87abf6beddc9e56c0", "50197163a22fd17f79086e087a787883b3ec9280a509807daf158dfc2a7ded02", "56b13000acf891f700f5067512b804d1ec8c301d627486c678b903859d07f798", "79388ae29c896299b3567965dbcd93255f175c17c6c7bca38614d12718c47466", "79fd5d3d62238c4f583b75d48d53cdae759fe04d4fb18fe8b371d88ad2b6f8be", "7fe3e2fde2bf1d7ce25ebcd2d3de3650b8d60d9a73ce6dcef36e20191291613d", "81042a24f67b96e4287774014fa27220d8a4d91af1043389e4d73892efc89ac6", "81326f1095c53111f8afc95da281e1414185f4a538609a77ca50bdfa39a6c207", "8873dc0d8f42142ea9f20c27bbdc485190fff93823c6795be661703369e5877d", "88d2cbcb0a112f47eef71eb95460b6995da18e6f8ca50c264585abc2c473154b", "91f2491aeab9599956c45a77c5666d323efdec790bfe23fcceafcd91105d585a", "979daa8655ae5a51e8e7a24e7d34e250ae8309fd9719490df92cbb2fe2b0422b", "9c871b006c878a890c6e44a5b2f3c6291335324b298c904dc0402ee92ee1f0be", "a6d092545e5af53e960465f652e00efbf5357adad177b2630d63978d85e46a72", "b5ed7837b923d1d71c4f587ae1539ccd96bfd6be9788f507dbe94dab5febbb5d", "ba259f68250f16d2444cbbfaddaa0bb20e1560a4fdaad50bece25c199e6af864", "be1d89614c6b6c36d7578496dc8625123bda2ff44f224cf8b1c45b810ee7383f", "c1b030a79749aa8d1f1486885040114ee56933b15ccfc90049ba266e4aa2139f", "c95bb147fab76f2ecde332d972d8f4138b8f2daee6c466af4ff3b4f29bd4c19e", "d52c1c2d7e856cecc05aa0526453cb14574f821b7f413cc279b9514750d795c1", "d609a6d564ad3d327e9509846c2c47f170456344521462b469e5cb39e48ba31c", "e1bad043c12fb58e8c7d92b3d7f2f49977dcb80a08a6d1e7a5114a11bf819fca", "e5a675f6829c53c87d79117a8eb656cc4a5f8918185a32fc93ba09778e90f6db", "fec32646b98baf4a22fdceb08703965bd16dea09051fbeb31a04b5b6e72b846c"] ]
entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] attrs = [
filelock = ["18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", "929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"] {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
flake8 = ["19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", "8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"] {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
importlib-metadata = ["aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"] ]
isort = ["54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", "6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"] cmakelang = [
mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] {file = "cmakelang-0.6.13-py3-none-any.whl", hash = "sha256:764b9467195c7c36453d60a829f30229720d26c7dffd41cb516b99bd9c7daf4e"},
more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"] {file = "cmakelang-0.6.13.tar.gz", hash = "sha256:03982e87b00654d024d73ef972d9d9bb0e5726cdb6b8a424a15661fb6278e67f"},
mypy = ["1521c186a3d200c399bd5573c828ea2db1362af7209b2adb1bb8532cea2fb36f", "31a046ab040a84a0fc38bc93694876398e62bc9f35eca8ccbf6418b7297f4c00", "3b1a411909c84b2ae9b8283b58b48541654b918e8513c20a400bb946aa9111ae", "48c8bc99380575deb39f5d3400ebb6a8a1cb5cc669bbba4d3bb30f904e0a0e7d", "540c9caa57a22d0d5d3c69047cc9dd0094d49782603eb03069821b41f9e970e9", "672e418425d957e276c291930a3921b4a6413204f53fe7c37cad7bc57b9a3391", "6ed3b9b3fdc7193ea7aca6f3c20549b377a56f28769783a8f27191903a54170f", "9371290aa2cad5ad133e4cdc43892778efd13293406f7340b9ffe99d5ec7c1d9", "ace6ac1d0f87d4072f05b5468a084a45b4eda970e4d26704f201e06d47ab2990", "b428f883d2b3fe1d052c630642cc6afddd07d5cd7873da948644508be3b9d4a7", "d5bf0e6ec8ba346a2cf35cb55bf4adfddbc6b6576fcc9e10863daa523e418dbb", "d7574e283f83c08501607586b3167728c58e8442947e027d2d4c7dcd6d82f453", "dc889c84241a857c263a2b1cd1121507db7d5b5f5e87e77147097230f374d10b", "f4748697b349f373002656bf32fede706a0e713d67bfdcf04edf39b1f61d46eb"] ]
mypy-extensions = ["090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", "2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"] colorama = [
packaging = ["28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", "d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"] {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
pluggy = ["0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", "fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"] {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"] ]
pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"] coverage = [
pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"] {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"},
pygls = ["3ee878a828b7bc0873a2ea44208d6846a91aa7dbbbdc052e7fe8cc689f6644fa", "780fd0c5ae95ad02ecaf70b071e43ff8ced8384b7d6bed19311a7b431d26fb88"] {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"},
pyparsing = ["6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", "d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"] {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"},
pytest = ["7e4800063ccfc306a53c461442526c5571e1462f61583506ce97e4da6a1d88c8", "ca563435f4941d0cb34767301c27bc65c510cb82e90b9ecf9cb52dc2c63caaa0"] {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"},
pytest-cov = ["cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b", "cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"] {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"},
pytest-datadir = ["1847ed0efe0bc54cac40ab3fba6d651c2f03d18dd01f2a582979604d32e7621e", "d3af1e738df87515ee509d6135780f25a15959766d9c2b2dbe02bf4fb979cb18"] {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"},
six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"},
toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"},
tox = ["0bc216b6a2e6afe764476b4a07edf2c1dab99ed82bb146a1130b2e828f5bff5e", "c4f6b319c20ba4913dbfe71ebfd14ff95d1853c4231493608182f66e566ecfe1"] {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"},
typed-ast = ["1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", "18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", "48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", "4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", "7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", "838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", "95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", "ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"] {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"},
typing = ["91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23", "c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36", "f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714"] {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"},
typing-extensions = ["2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95", "b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87", "d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed"] {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"},
virtualenv = ["11cb4608930d5fd3afb545ecf8db83fa50e1f96fc4fca80c94b07d2c83146589", "d257bb3773e48cac60e475a19b608996c73f4d333b3ba2e4e57d5ac6134e0136"] {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"},
wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"] {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"},
yapf = ["02ace10a00fa2e36c7ebd1df2ead91dbfbd7989686dc4ccbdc549e95d19f5780", "6f94b6a176a7c114cfa6bad86d40f259bbe0f10cf2fa7f2f4b3596fc5802a41b"] {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"},
zipp = ["3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", "f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"] {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"},
{file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"},
{file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"},
{file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"},
{file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"},
{file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"},
{file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"},
{file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"},
{file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"},
{file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"},
{file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"},
{file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"},
{file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"},
{file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"},
{file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"},
{file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"},
{file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"},
{file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"},
{file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"},
{file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"},
{file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"},
{file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"},
{file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"},
{file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"},
{file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"},
{file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"},
{file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"},
{file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"},
{file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"},
{file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"},
{file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"},
{file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"},
]
importlib-metadata = [
{file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"},
{file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pydantic = [
{file = "pydantic-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193"},
{file = "pydantic-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11"},
{file = "pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310"},
{file = "pydantic-1.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131"},
{file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580"},
{file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd"},
{file = "pydantic-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd"},
{file = "pydantic-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761"},
{file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918"},
{file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74"},
{file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a"},
{file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166"},
{file = "pydantic-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b"},
{file = "pydantic-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892"},
{file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e"},
{file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608"},
{file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537"},
{file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380"},
{file = "pydantic-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728"},
{file = "pydantic-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a"},
{file = "pydantic-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1"},
{file = "pydantic-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195"},
{file = "pydantic-1.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b"},
{file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49"},
{file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"},
{file = "pydantic-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0"},
{file = "pydantic-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6"},
{file = "pydantic-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810"},
{file = "pydantic-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f"},
{file = "pydantic-1.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee"},
{file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761"},
{file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd"},
{file = "pydantic-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1"},
{file = "pydantic-1.9.1-py3-none-any.whl", hash = "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58"},
{file = "pydantic-1.9.1.tar.gz", hash = "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a"},
]
pygls = [
{file = "pygls-0.12-py3-none-any.whl", hash = "sha256:93f8cd9458edb0cb8524588affa0f5fd7f75a4cdb5e2d450437b913e657df65b"},
{file = "pygls-0.12.tar.gz", hash = "sha256:452586db4e153f67d3e0505ceb98234e2cf1425c882f089fd458271a721b7809"},
]
pyparsing = [
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
]
pytest = [
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
{file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
]
pytest-cov = [
{file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"},
{file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"},
]
pytest-datadir = [
{file = "pytest-datadir-1.3.1.tar.gz", hash = "sha256:d3af1e738df87515ee509d6135780f25a15959766d9c2b2dbe02bf4fb979cb18"},
{file = "pytest_datadir-1.3.1-py2.py3-none-any.whl", hash = "sha256:1847ed0efe0bc54cac40ab3fba6d651c2f03d18dd01f2a582979604d32e7621e"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
typeguard = [
{file = "typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1"},
{file = "typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4"},
]
typing-extensions = [
{file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"},
{file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"},
]
zipp = [
{file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"},
{file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"},
]

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "cmake-language-server" name = "cmake-language-server"
version = "0.1.1" version = "0.1.6"
description = "CMake LSP Implementation" description = "CMake LSP Implementation"
license = "MIT" license = "MIT"
authors = ["regen"] authors = ["regen"]
@@ -18,24 +18,34 @@ classifiers = [
] ]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.7,<3.11"
pygls = "^0.8.1" pygls = "^0.12"
pyparsing = "^2.4" importlib-metadata = {version = "^4.8", python = "<3.8"}
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
flake8 = "^3.7" pytest = "^6.2"
mypy = "^0.740.0"
pytest = "^5.2"
yapf = "^0.28.0"
pytest-datadir = "^1.3" pytest-datadir = "^1.3"
tox = "^3.14" pytest-cov = "^2.11"
isort = "^4.3" cmakelang = "^0.6.13"
pytest-cov = "^2.8"
[tool.poetry.scripts] [tool.poetry.scripts]
cmake-format = "cmake_language_server.formatter:main"
cmake-language-server = "cmake_language_server.server:main" cmake-language-server = "cmake_language_server.server:main"
[build-system] [build-system]
requires = ["poetry>=0.12"] requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.pysen]
version = "0.10"
[tool.pysen.lint]
enable_black = true
enable_flake8 = true
enable_isort = true
enable_mypy = true
mypy_preset = "strict"
line_length = 88
py_version = "py37"
[[tool.pysen.lint.mypy_targets]]
paths = ["."]

View File

@@ -1 +1,6 @@
__version__ = '0.1.1' try:
import importlib.metadata as importlib_metadata
except ModuleNotFoundError:
import importlib_metadata
__version__ = importlib_metadata.version(__name__)

View File

@@ -12,10 +12,10 @@ logger = logging.getLogger(__name__)
def _tidy_doc(doc: str) -> str: def _tidy_doc(doc: str) -> str:
doc = doc.strip() doc = doc.strip()
doc = re.sub(r':.+?:`(.+?)`', r'\1', doc) doc = re.sub(r":.+?:`(.+?)`", r"\1", doc)
doc = re.sub(r'``([^`]+)``', r'`\1`', doc) doc = re.sub(r"``([^`]+)``", r"`\1`", doc)
doc = doc.replace('\n', ' ') doc = doc.replace("\n", " ")
doc = doc.replace('. ', '. ') doc = doc.replace(". ", ". ")
return doc return doc
@@ -25,7 +25,7 @@ class API(object):
_uuid: uuid.UUID _uuid: uuid.UUID
_builtin_commands: Dict[str, str] _builtin_commands: Dict[str, str]
_builtin_variables: Dict[str, str] _builtin_variables: Dict[str, str]
_builtin_variable_template: Dict[Pattern, str] _builtin_variable_template: Dict[Pattern[str], str]
_builtin_modules: Dict[str, str] _builtin_modules: Dict[str, str]
_targets: List[str] _targets: List[str]
_cached_variables: Dict[str, str] _cached_variables: Dict[str, str]
@@ -45,81 +45,97 @@ class API(object):
self._generated_list_parsed = False self._generated_list_parsed = False
def query(self) -> bool: def query(self) -> bool:
"""Use CMake's file API to get JSON information about the build tree
Generates a JSON request file for the current build tree and runs
CMake on the build tree. Deletes the request file immediately
after.
"""
if not self.cmake_cache.exists(): if not self.cmake_cache.exists():
return False return False
self.query_json.parent.mkdir(parents=True, exist_ok=True) self.query_json.parent.mkdir(parents=True, exist_ok=True)
with self.query_json.open('w') as fp: with self.query_json.open("w") as fp:
fp.write('''\ fp.write(
"""\
{ {
"requests": [ "requests": [
{"kind": "codemodel", "version": 2}, {"kind": "codemodel", "version": 2},
{"kind": "cache", "version": 2}, {"kind": "cache", "version": 2},
{"kind": "cmakeFiles", "version": 1} {"kind": "cmakeFiles", "version": 1}
] ]
}''') }"""
)
proc = subprocess.run([self._cmake, str(self._build)], proc = subprocess.run(
universal_newlines=True, [self._cmake, str(self._build)],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE,
encoding="utf-8",
universal_newlines=True,
)
self.query_json.unlink() self.query_json.unlink()
self.query_json.parent.rmdir() self.query_json.parent.rmdir()
if proc.returncode != 0: if proc.returncode != 0:
logging.error( logging.error(f"cmake exited with {proc.returncode}: {proc.stderr}")
f'cmake exited with {proc.returncode}: {proc.stderr}')
return False return False
return True return True
def read_reply(self) -> bool: def read_reply(self) -> bool:
reply = self._build / '.cmake' / 'api' / 'v1' / 'reply' """Reads the CMake file API reply file and updates internal state
indices = sorted(reply.glob('index-*.json'))
Reads the result of the previous query file and updates
the targets, the cache entries and the cmake files.
"""
reply = self._build / ".cmake" / "api" / "v1" / "reply"
indices = sorted(reply.glob("index-*.json"))
if not indices: if not indices:
logger.error('no reply') logger.error("no reply")
return False return False
with indices[-1].open() as fp: with indices[-1].open() as fp:
index = json.load(fp) index = json.load(fp)
try: try:
responses = index['reply'][f'client-{self._uuid}']['query.json'][ responses = index["reply"][f"client-{self._uuid}"]["query.json"][
'responses'] "responses"
]
except KeyError: except KeyError:
logger.error('no rensponse') logger.error("no rensponse")
return False return False
for response in responses: for response in responses:
if response['kind'] == 'codemodel': if response["kind"] == "codemodel":
self._read_codemodel(reply / response['jsonFile']) self._read_codemodel(reply / response["jsonFile"])
elif response['kind'] == 'cache': elif response["kind"] == "cache":
self._read_cache(reply / response['jsonFile']) self._read_cache(reply / response["jsonFile"])
elif response['kind'] == 'cmakeFiles': elif response["kind"] == "cmakeFiles":
self._read_cmake_files(reply / response['jsonFile']) self._read_cmake_files(reply / response["jsonFile"])
return True return True
def _read_codemodel(self, codemodelpath: Path): def _read_codemodel(self, codemodelpath: Path) -> None:
with (codemodelpath).open() as fp: with (codemodelpath).open() as fp:
codemodel = json.load(fp) codemodel = json.load(fp)
config = codemodel['configurations'][0] config = codemodel["configurations"][0]
self._targets[:] = [x['name'] for x in config['targets']] self._targets[:] = [x["name"] for x in config["targets"]]
def _read_cache(self, cachepath: Path): def _read_cache(self, cachepath: Path) -> None:
with cachepath.open() as fp: with cachepath.open() as fp:
cache = json.load(fp) cache = json.load(fp)
self._cached_variables.clear() self._cached_variables.clear()
for entry in cache['entries']: for entry in cache["entries"]:
name = entry['name'] name = entry["name"]
value = self._truncate_variable(entry['value']) value = self._truncate_variable(entry["value"])
properties = {x['name']: x['value'] for x in entry['properties']} properties = {x["name"]: x["value"] for x in entry["properties"]}
helpstring = properties.get('HELPSTRING', '') helpstring = properties.get("HELPSTRING", "")
doc = [] doc = []
if helpstring: if helpstring:
doc.append(helpstring) doc.append(helpstring)
if value: if value:
doc.append(f'`{value}`') doc.append(f"`{value}`")
self._cached_variables[name] = '\n\n'.join(doc) self._cached_variables[name] = "\n\n".join(doc)
def _read_cmake_files(self, jsonpath: Path): def _read_cmake_files(self, jsonpath: Path) -> None:
'''inspect generated list files''' """inspect CMake list files that are used during build generation"""
if not self._builtin_variables or self._generated_list_parsed: if not self._builtin_variables or self._generated_list_parsed:
return return
@@ -127,45 +143,48 @@ class API(object):
with jsonpath.open() as fp: with jsonpath.open() as fp:
cmake_files = json.load(fp) cmake_files = json.load(fp)
# inspect generated list files # Inspect generated list files: Get the values of variables in each script
with tempfile.TemporaryDirectory() as tmpdirname: with tempfile.TemporaryDirectory() as tmpdirname:
tmplist = Path(tmpdirname) / 'dump.cmake' tmplist = Path(tmpdirname) / "dump.cmake"
with tmplist.open('w') as fp: with tmplist.open("w") as fp:
for listfile in cmake_files['inputs']: for listfile in cmake_files["inputs"]:
if not listfile.get('isGenerated', False): if not listfile.get("isGenerated", False):
continue continue
path = listfile['path'] path = listfile["path"]
fp.write(f'include({path})\n') fp.write(f"include({path})\n")
fp.write(''' fp.write(
"""
get_cmake_property(variables VARIABLES) get_cmake_property(variables VARIABLES)
foreach (variable ${variables}) foreach (variable ${variables})
message("${variable}=${${variable}}") message("${variable}=${${variable}}")
endforeach() endforeach()
''') """
)
p = subprocess.run( p = subprocess.run(
[self._cmake, '-P', str(tmplist)], [self._cmake, "-P", str(tmplist)],
cwd=cmake_files['paths']['source'],
universal_newlines=True,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE,
cwd=cmake_files["paths"]["source"],
encoding="utf-8",
universal_newlines=True,
)
if p.returncode != 0: if p.returncode != 0:
return return
for line in p.stderr.split('\n'): for line in p.stderr.split("\n"):
line = line.strip() line = line.strip()
if not line: if not line:
continue continue
k, v = line.split('=', 1) k, v = line.split("=", 1)
if k.startswith('CMAKE_ARG'): if k.startswith("CMAKE_ARG"):
continue continue
v = self._truncate_variable(v) v = self._truncate_variable(v)
if k in self._builtin_variables: if k in self._builtin_variables:
self._builtin_variables[k] += f'\n\n`{v}`' self._builtin_variables[k] += f"\n\n`{v}`"
else: else:
for pattern, doc in self._builtin_variable_template.items( for pattern, doc in self._builtin_variable_template.items():
):
if pattern.fullmatch(k): if pattern.fullmatch(k):
self._builtin_variables[k] = f'{doc}\n\n`{v}`' self._builtin_variables[k] = f"{doc}\n\n`{v}`"
break break
else: else:
# ignore variable with no document # ignore variable with no document
@@ -175,12 +194,19 @@ endforeach()
@property @property
def query_json(self) -> Path: def query_json(self) -> Path:
return (self._build / '.cmake' / 'api' / 'v1' / 'query' / return (
f'client-{self._uuid}' / 'query.json') self._build
/ ".cmake"
/ "api"
/ "v1"
/ "query"
/ f"client-{self._uuid}"
/ "query.json"
)
@property @property
def cmake_cache(self) -> Path: def cmake_cache(self) -> Path:
return self._build / 'CMakeCache.txt' return self._build / "CMakeCache.txt"
def parse_doc(self) -> None: def parse_doc(self) -> None:
self._parse_commands() self._parse_commands()
@@ -188,78 +214,110 @@ endforeach()
self._parse_modules() self._parse_modules()
def _parse_commands(self) -> None: def _parse_commands(self) -> None:
p = subprocess.run([self._cmake, '--help-commands'], """Load docs for builtin cmake functions
stdout=subprocess.PIPE,
universal_newlines=True) Loads the documentation for builtin cmake functions from the result
of `$ cmake --help-commands`.
"""
p = subprocess.run(
[self._cmake, "--help-commands"],
stdout=subprocess.PIPE,
encoding="utf-8",
universal_newlines=True,
)
if p.returncode != 0: if p.returncode != 0:
return return
matches = re.finditer( matches = re.finditer(
r''' r"""
(?P<command>.+)\n (?P<command>.+)\n
-+\n+? -+\n+?
[\s\S]*? [\s\S]*?
(?P<signature>(?P=command)\s*\([^)]*\)) (?P<signature>(?P=command)\s*\([^)]*\))
''', p.stdout, re.VERBOSE) """,
p.stdout,
re.VERBOSE,
)
self._builtin_commands.clear() self._builtin_commands.clear()
for match in matches: for match in matches:
command = match.group('command') command = match.group("command")
signature = match.group('signature') signature = match.group("signature")
signature = re.sub(r'^ ', r'', signature, flags=re.MULTILINE) signature = re.sub(r"^ ", r"", signature, flags=re.MULTILINE)
self._builtin_commands[ self._builtin_commands[command] = "```cmake\n" + signature + "\n```"
command] = '```cmake\n' + signature + '\n```'
def _parse_variables(self) -> None: def _parse_variables(self) -> None:
p = subprocess.run([self._cmake, '--help-variables'], """Load docs for builtin cmake variables
stdout=subprocess.PIPE,
universal_newlines=True) Loads the documentation for builtin cmake variables from
the result of `$ cmake --help-variables`.
"""
p = subprocess.run(
[self._cmake, "--help-variables"],
stdout=subprocess.PIPE,
encoding="utf-8",
universal_newlines=True,
)
if p.returncode != 0: if p.returncode != 0:
return return
matches = re.finditer( matches = re.finditer(
r''' r"""
(?P<variable>.+)\n (?P<variable>.+)\n
-+\n\n -+\n\n
(?P<doc>[\s\S]+?)(?:\n\n|$) (?P<doc>[\s\S]+?)(?:\n\n|$)
''', p.stdout, re.VERBOSE) """,
p.stdout,
re.VERBOSE,
)
self._builtin_variables.clear() self._builtin_variables.clear()
for match in matches: for match in matches:
variable = match.group('variable') variable = match.group("variable")
doc = _tidy_doc(match.group('doc')) doc = _tidy_doc(match.group("doc"))
if variable == 'CMAKE_MATCH_<n>': if variable == "CMAKE_MATCH_<n>":
for i in range(10): for i in range(10):
self._builtin_variables[f'CMAKE_MATCH_{i}'] = doc self._builtin_variables[f"CMAKE_MATCH_{i}"] = doc
elif '<' in variable: elif "<" in variable:
variable = re.sub(r'<[^>]+>', r'[^_]+', variable) variable = re.sub(r"<[^>]+>", r"[^_]+", variable)
pattern = re.compile(variable) pattern = re.compile(variable)
self._builtin_variable_template[pattern] = doc self._builtin_variable_template[pattern] = doc
else: else:
self._builtin_variables[variable] = doc self._builtin_variables[variable] = doc
def _parse_modules(self) -> None: def _parse_modules(self) -> None:
p = subprocess.run([self._cmake, '--help-modules'], """Loads docs for all modules in the cmake distribution
stdout=subprocess.PIPE,
universal_newlines=True) Loads the documentation for cmake modules included in the
distribution from the result of `$ cmake --help-modules`.
"""
p = subprocess.run(
[self._cmake, "--help-modules"],
stdout=subprocess.PIPE,
encoding="utf-8",
universal_newlines=True,
)
if p.returncode != 0: if p.returncode != 0:
return return
matches = re.finditer( matches = re.finditer(
r''' r"""
(?P<module>.+)\n (?P<module>.+)\n
-+\n+? -+\n+?
(?:(?P<header>\w[\w\s]+)\n\^+\n+?)? (?:(?P<header>\w[\w\s]+)\n\^+\n+?)?
(?P<doc>.(?:.|\n)+?\n\n) (?P<doc>(?:.|\n)*?\n\n)
''', p.stdout + '\n\n', re.VERBOSE) """,
p.stdout + "\n\n",
re.VERBOSE,
)
self._builtin_modules.clear() self._builtin_modules.clear()
for match in matches: for match in matches:
module = match.group('module') module = match.group("module")
header = match.group('header') header = match.group("header")
doc = _tidy_doc(match.group('doc')) doc = _tidy_doc(match.group("doc"))
if header is not None and header != 'Overview': if header != "Overview":
doc = '' doc = ""
self._builtin_modules[module] = doc self._builtin_modules[module] = doc
def get_command_doc(self, command: str) -> Optional[str]: def get_command_doc(self, command: str) -> Optional[str]:
@@ -276,28 +334,27 @@ endforeach()
return self._builtin_variables.get(variable) return self._builtin_variables.get(variable)
def search_variable(self, variable: str) -> List[str]: def search_variable(self, variable: str) -> List[str]:
cached = frozenset(x for x in self._cached_variables cached = frozenset(x for x in self._cached_variables if x.startswith(variable))
if x.startswith(variable)) builtin = frozenset(
builtin = frozenset(x for x in self._builtin_variables x for x in self._builtin_variables if x.startswith(variable)
if x.startswith(variable)) )
return list(cached | builtin) return list(cached | builtin)
def get_module_doc(self, module: str, package: bool) -> Optional[str]: def get_module_doc(self, module: str, package: bool) -> Optional[str]:
if package: if package:
return self._builtin_modules.get('Find' + module) return self._builtin_modules.get("Find" + module)
return self._builtin_modules.get(module) return self._builtin_modules.get(module)
def search_module(self, module: str, package: bool) -> List[str]: def search_module(self, module: str, package: bool) -> List[str]:
if package: if package:
module = 'Find' + module module = "Find" + module
return [ return [x[4:] for x in self._builtin_modules if x.startswith(module)]
x[4:] for x in self._builtin_modules if x.startswith(module)
]
return [ return [
x for x in self._builtin_modules x
if x.startswith(module) and not x.startswith('Find') for x in self._builtin_modules
if x.startswith(module) and not x.startswith("Find")
] ]
def search_target(self, target: str) -> List[str]: def search_target(self, target: str) -> List[str]:
@@ -305,4 +362,4 @@ endforeach()
def _truncate_variable(self, v: str) -> str: def _truncate_variable(self, v: str) -> str:
width = 70 width = 70
return v[:width] + (v[width:] and '...') return v[:width] + (v[width:] and "...")

View File

@@ -1,143 +0,0 @@
from typing import List
from .parser import TokenList
class Formatter(object):
indnt: str
lower_identifier: bool
def __init__(self, indent=' ', lower_identifier=True):
self.indent = indent
self.lower_identifier = lower_identifier
def format(self, tokens: TokenList) -> str:
cmds: List[str] = ['']
indnet_level = 0
for token in tokens:
if isinstance(token, tuple):
raw_identifier = token[0]
identifier = raw_identifier.lower()
if identifier in ('elseif', 'else', 'endif', 'endforeach',
'endwhile', 'endmacro', 'endfunction'):
if indnet_level > 0:
indnet_level -= 1
cmds[-1] = self.indent * indnet_level
cmds[-1] += (identifier
if self.lower_identifier else raw_identifier)
args = self._format_args(token[1])
if len(args) < 2:
cmds[-1] += '(' + ''.join(args) + ')'
else:
cmds[-1] += '(\n'
for arg in args:
cmds[-1] += self.indent * (indnet_level +
1) + arg + '\n'
cmds[-1] += self.indent * indnet_level + ')'
if identifier in ('if', 'elseif', 'else', 'foreach', 'while',
'macro', 'function'):
indnet_level += 1
elif token == '\n':
cmds.append('')
elif token[0] == '#':
if cmds[-1]:
cmds[-1] += token
else:
cmds[-1] = self.indent * indnet_level + token
elif cmds[-1]:
cmds[-1] += token
cmds = self._strip_line(cmds)
return '\n'.join(cmds) + '\n'
def _format_args(self, args: List[str]) -> List[str]:
lines = ['']
for i in range(len(args)):
arg = args[i]
if arg[0] == '#':
lines[-1] += arg
elif arg[0] == '\n':
lines.append('')
elif arg.isspace():
if lines[-1]:
if i + 1 < len(args) and args[i + 1][0] == '#':
lines[-1] += arg
else:
lines[-1] += ' '
else:
lines[-1] += arg
return self._strip_line(lines)
def _strip_line(self, lines: List[str]) -> List[str]:
'''Delete empty lines at the start/end of the input'''
ret: List[str] = []
for line in lines:
line = line.rstrip()
if line != '' or len(ret) > 0:
ret.append(line)
while ret and ret[-1] == '':
del ret[-1]
return ret
def main(args: List[str] = None):
from argparse import ArgumentParser
from difflib import unified_diff
from pathlib import Path
import sys
from . import __version__
from .parser import ListParser
parser = ArgumentParser(
description='Format CMake list files.',
epilog='''
If no arguments are specified, it formats the code from
standard input and writes the result to the standard output.''',
)
parser.add_argument('lists', type=Path, nargs='*', help='CMake list files')
group = parser.add_mutually_exclusive_group()
group.add_argument('-i',
'--inplace',
action='store_true',
help='inplace edit')
group.add_argument('-d', '--diff', action='store_true', help='show diff')
parser.add_argument('--version',
action='version',
version=f'%(prog)s {__version__}')
args = parser.parse_args(args)
if not args.lists and args.inplace:
print('error: cannot use -i when no arguments are specified.',
file=sys.stderr)
return
if not args.lists:
args.lists.append(None)
list_parser = ListParser()
formatter = Formatter()
for listpath in args.lists:
if listpath is None:
listpath = '(stdin)'
content = sys.stdin.read()
else:
with listpath.open() as fp:
content = fp.read()
tokens, remain = list_parser.parse(content)
formatted = content if remain else formatter.format(tokens)
if args.inplace:
if not remain:
with listpath.open('w') as fp:
fp.write(formatted)
elif args.diff:
diff = unified_diff(content.splitlines(True),
formatted.splitlines(True), str(listpath),
str(listpath), '(before formatting)',
'(after formatting)')
diffstr = ''.join(diff)
print(diffstr, end='')
else:
print(formatted, end='')

View File

@@ -1,63 +0,0 @@
from typing import List, Tuple, Union
import pyparsing as pp
CommandTokenType = Tuple[str, List[str]]
TokenType = Union[str, CommandTokenType]
TokenList = List[TokenType]
class ListParser(object):
_parser: pp.ParserElement
def __init__(self):
newline = '\n'
space_plus = pp.Regex('[ \t]+')
space_star = pp.Optional(space_plus)
quoted_element = pp.Regex(r'[^\\"]|\\[^A-Za-z0-9]|\\[trn]')
quoted_argument = pp.Combine('"' + pp.ZeroOrMore(quoted_element) + '"')
bracket_content = pp.Forward()
def action_bracket_open(tokens: pp.ParseResults):
nonlocal bracket_content
marker = ']' + '=' * (len(tokens[0]) - 2) + ']'
bracket_content <<= pp.SkipTo(marker, include=True)
bracket_open = pp.Regex(r'\[=*\[').setParseAction(action_bracket_open)
bracket_argument = pp.Combine(bracket_open + bracket_content)
unquoted_element = pp.Regex(r'[^\s()#"\\]|\\[^A-Za-z0-9]|\\[trn]')
unquoted_argument = pp.Combine(pp.OneOrMore(unquoted_element))
argument = bracket_argument | quoted_argument | unquoted_argument
line_comment = pp.Combine('#' + ~bracket_open +
pp.SkipTo(pp.LineEnd()))
bracket_comment = pp.Combine('#' + bracket_argument)
line_ending = (space_star +
pp.ZeroOrMore(bracket_comment + space_star) +
pp.Optional(line_comment) + (newline | pp.lineEnd))
identifier = pp.Word(pp.alphas + '_', pp.alphanums + '_')
arguments = pp.Forward()
arguments << pp.ZeroOrMore(argument | line_ending | space_plus
| '(' + arguments + ')').leaveWhitespace()
arguments = pp.Group(arguments)
PAREN_L, PAREN_R = map(pp.Suppress, '()')
command_invocation = (
identifier + space_star.suppress() + PAREN_L + arguments +
PAREN_R).setParseAction(lambda t: (t[0], t[1].asList()))
file_element = (space_star + command_invocation + line_ending
| line_ending).leaveWhitespace()
file = pp.ZeroOrMore(file_element)
self._parser = file
def parse(self, liststr: str) -> Tuple[TokenList, str]:
for t, s, e in self._parser.scanString(liststr, maxMatches=1):
if s == 0:
return t.asList(), liststr[e:]
return [], liststr

View File

@@ -1,178 +1,259 @@
import logging import logging
import re import re
import shutil
import subprocess
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple from typing import Any, Callable, List, Optional, Tuple
from pygls.features import (COMPLETION, FORMATTING, HOVER, INITIALIZE, from pygls.lsp.methods import (
INITIALIZED, TEXT_DOCUMENT_DID_SAVE) COMPLETION,
FORMATTING,
HOVER,
INITIALIZE,
INITIALIZED,
TEXT_DOCUMENT_DID_SAVE,
)
from pygls.lsp.types import (
CompletionItem,
CompletionItemKind,
CompletionList,
CompletionOptions,
CompletionParams,
CompletionTriggerKind,
DocumentFormattingParams,
Hover,
InitializeParams,
MarkupContent,
MarkupKind,
Position,
Range,
TextDocumentPositionParams,
TextDocumentSaveRegistrationOptions,
TextEdit,
)
from pygls.server import LanguageServer from pygls.server import LanguageServer
from pygls.types import (CompletionItem, CompletionItemKind, CompletionList,
CompletionParams, CompletionTriggerKind,
DocumentFormattingParams, Hover, InitializeParams,
MarkupContent, MarkupKind, Position, Range,
TextDocumentPositionParams, TextEdit)
from .api import API from .api import API
from .formatter import Formatter
from .parser import ListParser
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class CMakeLanguageServer(LanguageServer): class CMakeLanguageServer(LanguageServer):
_parser: ListParser _api: Optional[API]
_api: API
def __init__(self, *args): def __init__(self, *args: Any) -> None:
super().__init__(*args) super().__init__(*args)
self._parser = ListParser()
self._api = None self._api = None
@self.feature(INITIALIZE) @self.feature(INITIALIZE)
def initialize(params: InitializeParams): def initialize(params: InitializeParams) -> None:
opts = params.initializationOptions opts = params.initialization_options
cmake = getattr(opts, 'cmakeExecutable', 'cmake') cmake = getattr(opts, "cmakeExecutable", "cmake")
builddir = getattr(opts, 'buildDirectory', '') builddir = getattr(opts, "buildDirectory", "")
logging.info(f'cmakeExecutable={cmake}, buildDirectory={builddir}') logging.info(f"cmakeExecutable={cmake}, buildDirectory={builddir}")
self._api = API(cmake, Path(builddir)) self._api = API(cmake, Path(builddir))
self._api.parse_doc() self._api.parse_doc()
@self.feature(COMPLETION, trigger_characters=['{', '(']) trigger_characters = ["{", "("]
def completions(params: CompletionParams):
if (params.context.triggerKind == @self.feature(
CompletionTriggerKind.TriggerCharacter): COMPLETION, CompletionOptions(trigger_characters=trigger_characters)
token = '' )
trigger = params.context.triggerCharacter def completions(params: CompletionParams) -> CompletionList:
assert self._api is not None
if (
params.context is not None
and params.context.trigger_kind
== CompletionTriggerKind.TriggerCharacter
):
token = ""
trigger = params.context.trigger_character
else: else:
word = self._cursor_word(params.textDocument.uri, line = self._cursor_line(params.text_document.uri, params.position)
params.position, False) idx = params.position.character - 1
token = '' if word is None else word[0] if 0 <= idx < len(line) and line[idx] in trigger_characters:
trigger = None token = ""
trigger = line[idx]
else:
word = self._cursor_word(
params.text_document.uri, params.position, False
)
token = "" if word is None else word[0]
trigger = None
items: List[CompletionItem] = [] items: List[CompletionItem] = []
if trigger is None: if trigger is None:
commands = self._api.search_command(token) commands = self._api.search_command(token)
items.extend( items.extend(
CompletionItem(x, CompletionItem(
CompletionItemKind.Function, label=x,
documentation=self._api.get_command_doc(x)) kind=CompletionItemKind.Function,
for x in commands) documentation=self._api.get_command_doc(x),
insert_text=x,
)
for x in commands
)
if trigger is None or trigger == '{': if trigger is None or trigger == "{":
variables = self._api.search_variable(token) variables = self._api.search_variable(token)
items.extend( items.extend(
CompletionItem(x, CompletionItem(
CompletionItemKind.Variable, label=x,
documentation=self._api.get_variable_doc(x)) kind=CompletionItemKind.Variable,
for x in variables) documentation=self._api.get_variable_doc(x),
insert_text=x,
)
for x in variables
)
if trigger is None: if trigger is None:
targets = self._api.search_target(token) targets = self._api.search_target(token)
items.extend( items.extend(
CompletionItem(x, CompletionItemKind.Class) CompletionItem(
for x in targets) label=x, kind=CompletionItemKind.Class, insert_text=x
)
for x in targets
)
if trigger == '(': if trigger == "(":
func = self._cursor_function(params.textDocument.uri, func = self._cursor_function(params.text_document.uri, params.position)
params.position)
if func is not None: if func is not None:
func = func.lower() func = func.lower()
if func == 'include': if func == "include":
modules = self._api.search_module(token, False) modules = self._api.search_module(token, False)
items.extend( items.extend(
CompletionItem(x, CompletionItem(
CompletionItemKind.Module, label=x,
documentation=self._api. kind=CompletionItemKind.Module,
get_module_doc(x, False)) documentation=self._api.get_module_doc(x, False),
for x in modules) insert_text=x,
elif func == 'find_package': )
for x in modules
)
elif func == "find_package":
modules = self._api.search_module(token, True) modules = self._api.search_module(token, True)
items.extend( items.extend(
CompletionItem(x, CompletionItem(
CompletionItemKind.Module, label=x,
documentation=self._api. kind=CompletionItemKind.Module,
get_module_doc(x, True)) documentation=self._api.get_module_doc(x, True),
for x in modules) insert_text=x,
)
for x in modules
)
return CompletionList(False, items) return CompletionList(is_incomplete=False, items=items)
@self.feature(FORMATTING) if shutil.which("cmake-format") is not None:
def formatting(params: DocumentFormattingParams):
doc = self.workspace.get_document(params.textDocument.uri)
content = doc.source
tokens, remain = self._parser.parse(content)
if remain:
self.show_message('CMake parser failed')
return None
formatted = Formatter().format(tokens) @self.feature(FORMATTING)
lines = content.count('\n') def formatting(
return [ params: DocumentFormattingParams,
TextEdit(Range(Position(0, 0), Position(lines + 1, 0)), ) -> Optional[List[TextEdit]]:
formatted) doc = self.workspace.get_document(params.text_document.uri)
] content = doc.source
formatted = subprocess.check_output(
["cmake-format", "-"],
cwd=str(Path(doc.path).parent),
input=content,
universal_newlines=True,
)
lines = content.count("\n")
return [
TextEdit(
range=Range(
start=Position(line=0, character=0),
end=Position(line=lines + 1, character=0),
),
new_text=formatted,
)
]
@self.feature(HOVER) @self.feature(HOVER)
def hover(params: TextDocumentPositionParams): def hover(params: TextDocumentPositionParams) -> Optional[Hover]:
word = self._cursor_word(params.textDocument.uri, params.position, assert self._api is not None
True) api = self._api
word = self._cursor_word(params.text_document.uri, params.position, True)
if not word: if not word:
return None return None
candidates = [ candidates: List[Callable[[str], Optional[str]]] = [
lambda x: self._api.get_command_doc(x.lower()), lambda x: api.get_command_doc(x.lower()),
lambda x: self._api.get_variable_doc(x), lambda x: api.get_variable_doc(x),
lambda x: self._api.get_module_doc(x, False), lambda x: api.get_module_doc(x, False),
lambda x: self._api.get_module_doc(x, True), lambda x: api.get_module_doc(x, True),
] ]
for c in candidates: for c in candidates:
doc = c(word[0]) doc = c(word[0])
if doc is None: if doc is None:
continue continue
return Hover(MarkupContent(MarkupKind.Markdown, doc), word[1]) return Hover(
contents=MarkupContent(kind=MarkupKind.Markdown, value=doc),
range=word[1],
)
return None return None
@self.thread() @self.thread()
@self.feature(TEXT_DOCUMENT_DID_SAVE, includeText=False) @self.feature(
TEXT_DOCUMENT_DID_SAVE,
TextDocumentSaveRegistrationOptions(include_text=False),
)
@self.feature(INITIALIZED) @self.feature(INITIALIZED)
def run_cmake(*args): def run_cmake(*args: Any) -> None:
assert self._api is not None
if self._api.query(): if self._api.query():
self._api.read_reply() self._api.read_reply()
def _cursor_function(self, uri: str, position: Position) -> Optional[str]: def _cursor_function(self, uri: str, position: Position) -> Optional[str]:
doc = self.workspace.get_document(uri) doc = self.workspace.get_document(uri)
lines = doc.source.split('\n')[:position.line + 1] lines = doc.source.split("\n")[: position.line + 1]
lines[-1] = lines[-1][:position.character - 1].strip() lines[-1] = lines[-1][: position.character - 1].strip()
words = re.split(r'[\s\n()]+', '\n'.join(lines)) words = re.split(r"[\s\n()]+", "\n".join(lines))
return words[-1] if words else None return words[-1] if words else None
def _cursor_line(self, uri: str, position: Position) -> str: def _cursor_line(self, uri: str, position: Position) -> str:
doc = self.workspace.get_document(uri) doc = self.workspace.get_document(uri)
content = doc.source content = doc.source
line = content.split('\n')[position.line] line = content.split("\n")[position.line]
return line return str(line)
def _cursor_word(self, def _cursor_word(
uri: str, self, uri: str, position: Position, include_all: bool = True
position: Position, ) -> Optional[Tuple[str, Range]]:
include_all: bool = True) -> Optional[Tuple[str, Range]]:
line = self._cursor_line(uri, position) line = self._cursor_line(uri, position)
cursor = position.character cursor = position.character
for m in re.finditer(r'\w+', line): for m in re.finditer(r"\w+", line):
end = m.end() if include_all else cursor end = m.end() if include_all else cursor
if m.start() <= cursor <= m.end(): if m.start() <= cursor <= m.end():
word = (line[m.start():end], word = (
Range(Position(position.line, m.start()), line[m.start() : end],
Position(position.line, end))) Range(
start=Position(line=position.line, character=m.start()),
end=Position(line=position.line, character=end),
),
)
return word return word
return None return None
def main(): def main() -> None:
from argparse import ArgumentParser
from . import __version__
parser = ArgumentParser(description="CMake Language Server")
parser.add_argument(
"--version", action="version", version=f"%(prog)s {__version__}"
)
parser.parse_args()
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logging.getLogger('pygls').setLevel(logging.WARNING) logging.getLogger("pygls").setLevel(logging.WARNING)
CMakeLanguageServer().start_io() CMakeLanguageServer().start_io() # type: ignore

View File

@@ -2,46 +2,49 @@ import asyncio
import logging import logging
import os import os
import pprint import pprint
from pathlib import Path
from subprocess import PIPE, run from subprocess import PIPE, run
from threading import Thread from threading import Thread
from typing import Iterable, Tuple
import pytest import pytest
from pygls import features
from pygls.server import LanguageServer
from cmake_language_server.server import CMakeLanguageServer from cmake_language_server.server import CMakeLanguageServer
from pygls.lsp.methods import EXIT
from pygls.server import LanguageServer
@pytest.fixture() @pytest.fixture()
def cmake_build(shared_datadir): def cmake_build(shared_datadir: Path) -> Iterable[Path]:
source = shared_datadir / 'cmake' source = shared_datadir / "cmake"
build = source / 'build' build = source / "build"
build.mkdir() build.mkdir()
p = run(['cmake', str(source)], p = run(
cwd=build, ["cmake", str(source)],
stdout=PIPE, cwd=build,
stderr=PIPE, stdout=PIPE,
universal_newlines=True) stderr=PIPE,
universal_newlines=True,
)
if p.returncode != 0: if p.returncode != 0:
logging.error('env:\n' + pprint.pformat(os.environ)) logging.error("env:\n" + pprint.pformat(os.environ))
logging.error('stdout:\n' + p.stdout) logging.error("stdout:\n" + p.stdout)
logging.error('stderr:\n' + p.stderr) logging.error("stderr:\n" + p.stderr)
raise RuntimeError("CMake failed") raise RuntimeError("CMake failed")
yield build yield build
@pytest.fixture() @pytest.fixture()
def client_server(): def client_server() -> Iterable[Tuple[LanguageServer, CMakeLanguageServer]]:
c2s_r, c2s_w = os.pipe() c2s_r, c2s_w = os.pipe()
s2c_r, s2c_w = os.pipe() s2c_r, s2c_w = os.pipe()
def start(ls: LanguageServer, fdr, fdw): def start(ls: LanguageServer, fdr: int, fdw: int) -> None:
# TODO: better patch is needed # TODO: better patch is needed
# disable `close()` to avoid error messages # disable `close()` to avoid error messages
close = ls.loop.close close = ls.loop.close
ls.loop.close = lambda: None ls.loop.close = lambda: None # type: ignore
ls.start_io(os.fdopen(fdr, 'rb'), os.fdopen(fdw, 'wb')) ls.start_io(os.fdopen(fdr, "rb"), os.fdopen(fdw, "wb")) # type: ignore
ls.loop.close = close ls.loop.close = close # type: ignore
server = CMakeLanguageServer(asyncio.new_event_loop()) server = CMakeLanguageServer(asyncio.new_event_loop())
server_thread = Thread(target=start, args=(server, c2s_r, s2c_w)) server_thread = Thread(target=start, args=(server, c2s_r, s2c_w))
@@ -53,7 +56,7 @@ def client_server():
yield client, server yield client, server
client.send_notification(features.EXIT) client.send_notification(EXIT)
server.send_notification(features.EXIT) server.send_notification(EXIT)
server_thread.join() server_thread.join()
client_thread.join() client_thread.join()

View File

@@ -1,110 +1,122 @@
import subprocess import subprocess
from pathlib import Path
from cmake_language_server.api import API from cmake_language_server.api import API
def test_query_with_cache(cmake_build): def test_query_with_cache(cmake_build: Path) -> None:
api = API('cmake', cmake_build) api = API("cmake", cmake_build)
assert api.query() assert api.query()
query = cmake_build / '.cmake' / 'api' / 'v1' / 'query' query = cmake_build / ".cmake" / "api" / "v1" / "query"
assert query.exists() assert query.exists()
reply = cmake_build / '.cmake' / 'api' / 'v1' / 'reply' reply = cmake_build / ".cmake" / "api" / "v1" / "reply"
assert reply.exists() assert reply.exists()
def test_query_without_cache(cmake_build): def test_query_without_cache(cmake_build: Path) -> None:
api = API('cmake', cmake_build) api = API("cmake", cmake_build)
(cmake_build / 'CMakeCache.txt').unlink() (cmake_build / "CMakeCache.txt").unlink()
assert not api.query() assert not api.query()
def test_read_variable(cmake_build): def test_read_variable(cmake_build: Path) -> None:
api = API('cmake', cmake_build) api = API("cmake", cmake_build)
assert api.query() assert api.query()
assert api.read_reply() assert api.read_reply()
assert api.get_variable_doc('testproject_BINARY_DIR') assert api.get_variable_doc("testproject_BINARY_DIR")
def test_read_cmake_files(cmake_build): def test_read_cmake_files(cmake_build: Path) -> None:
api = API('cmake', cmake_build) api = API("cmake", cmake_build)
api.parse_doc() api.parse_doc()
assert api.query() assert api.query()
api.read_reply() api.read_reply()
import platform import platform
system = platform.system() system = platform.system()
if system == 'Linux': cxx = api.get_variable_doc("CMAKE_CXX_COMPILER_ID")
assert 'GNU' in api.get_variable_doc('CMAKE_CXX_COMPILER_ID') assert cxx is not None
elif system == 'Windows': if system == "Linux":
assert 'MSVC' in api.get_variable_doc('CMAKE_CXX_COMPILER_ID') assert "GNU" in cxx
elif system == "Windows":
assert "MSVC" in cxx
elif system == "Darwin":
assert "Clang" in cxx
else: else:
raise RuntimeError('Unexpected system') raise RuntimeError("Unexpected system")
def test_parse_commands(cmake_build): def test_parse_commands(cmake_build: Path) -> None:
api = API('cmake', cmake_build) api = API("cmake", cmake_build)
api.parse_doc() api.parse_doc()
p = subprocess.run(['cmake', '--help-command-list'], p = subprocess.run(
universal_newlines=True, ["cmake", "--help-command-list"],
stdout=subprocess.PIPE, universal_newlines=True,
stderr=subprocess.PIPE) stdout=subprocess.PIPE,
commands = p.stdout.strip().split('\n') stderr=subprocess.PIPE,
)
commands = p.stdout.strip().split("\n")
for command in commands: for command in commands:
assert api.get_command_doc(command) is not None, f'{command} not found' assert api.get_command_doc(command) is not None, f"{command} not found"
assert 'break()' in api.get_command_doc('break') break_doc = api.get_command_doc("break")
assert api.get_command_doc('not_existing_command') is None assert break_doc is not None and "break()" in break_doc
assert api.get_command_doc("not_existing_command") is None
def test_parse_variables(cmake_build): def test_parse_variables(cmake_build: Path) -> None:
api = API('cmake', cmake_build) api = API("cmake", cmake_build)
api.parse_doc() api.parse_doc()
p = subprocess.run(['cmake', '--help-variable-list'], p = subprocess.run(
universal_newlines=True, ["cmake", "--help-variable-list"],
stdout=subprocess.PIPE, universal_newlines=True,
stderr=subprocess.PIPE) stdout=subprocess.PIPE,
variables = p.stdout.strip().split('\n') stderr=subprocess.PIPE,
)
variables = p.stdout.strip().split("\n")
for variable in variables: for variable in variables:
if '<' in variable: if "<" in variable:
continue continue
assert api.get_variable_doc( assert api.get_variable_doc(variable) is not None, f"{variable} not found"
variable) is not None, f'{variable} not found'
assert api.get_variable_doc('BUILD_SHARED_LIBS') is not None assert api.get_variable_doc("BUILD_SHARED_LIBS") is not None
assert api.get_variable_doc('not_existing_variable') is None assert api.get_variable_doc("not_existing_variable") is None
def test_parse_modules(cmake_build): def test_parse_modules(cmake_build: Path) -> None:
api = API('cmake', cmake_build) api = API("cmake", cmake_build)
api.parse_doc() api.parse_doc()
p = subprocess.run(['cmake', '--help-module-list'], p = subprocess.run(
universal_newlines=True, ["cmake", "--help-module-list"],
stdout=subprocess.PIPE, universal_newlines=True,
stderr=subprocess.PIPE) stdout=subprocess.PIPE,
modules = p.stdout.strip().split('\n') stderr=subprocess.PIPE,
)
modules = p.stdout.strip().split("\n")
for module in modules: for module in modules:
if module.startswith('Find'): if module.startswith("Find"):
assert api.get_module_doc(module[4:], assert (
True) is not None, f'{module} not found' api.get_module_doc(module[4:], True) is not None
), f"{module} not found"
else: else:
assert api.get_module_doc(module, assert api.get_module_doc(module, False) is not None, f"{module} not found"
False) is not None, f'{module} not found'
assert api.get_module_doc('GoogleTest', False) is not None assert api.get_module_doc("GoogleTest", False) is not None
assert api.get_module_doc('GoogleTest', True) is None assert api.get_module_doc("GoogleTest", True) is None
assert api.search_module('GoogleTest', False) == ['GoogleTest'] assert api.search_module("GoogleTest", False) == ["GoogleTest"]
assert api.search_module('GoogleTest', True) == [] assert api.search_module("GoogleTest", True) == []
assert api.get_module_doc('Boost', False) is None assert api.get_module_doc("Boost", False) is None
assert api.get_module_doc('Boost', True) is not None assert api.get_module_doc("Boost", True) is not None
assert api.search_module('Boost', False) == [] assert api.search_module("Boost", False) == []
assert api.search_module('Boost', True) == ['Boost'] assert api.search_module("Boost", True) == ["Boost"]

View File

@@ -1,156 +0,0 @@
import sys
from contextlib import contextmanager
from io import StringIO
from cmake_language_server.formatter import Formatter, main
from cmake_language_server.parser import ListParser
def make_formatter_test(liststr: str, expect: str):
def test():
tokens, remain = ListParser().parse(liststr)
actual = Formatter().format(tokens)
assert actual == expect
return test
test_command = make_formatter_test('a()', 'a()\n')
test_command_tolower = make_formatter_test('A()', 'a()\n')
test_remove_space = make_formatter_test('''
#a
b ( c ) # d
''', '''\
#a
b(c) # d
''')
test_indent_if = make_formatter_test(
'''
if()
a() # a
else()
# b
b()
endif()
''', '''\
if()
a() # a
else()
# b
b()
endif()
''')
test_indent_if_nested = make_formatter_test(
'''
if()
if()
a()
b()
endif()
endif()
''', '''\
if()
if()
a()
b()
endif()
endif()
''')
test_argument = make_formatter_test('a( b c d)', 'a(b c d)\n')
test_argument_multiline = make_formatter_test(
'''
if()
a(b c
d # e
f
# g
) # h
endif()
''', '''\
if()
a(
b c
d # e
f
# g
) # h
endif()
''')
@contextmanager
def mock_stdin(buf: str):
stdin = sys.stdin
sys.stdin = StringIO(buf)
yield
sys.stdin = stdin
def test_main_stdin(capsys):
with mock_stdin(' a()'):
main([])
captured = capsys.readouterr()
assert captured.out == 'a()\n'
assert captured.err == ''
def test_main_stdin_diff(capsys):
with mock_stdin(' a()'):
main(['-d'])
captured = capsys.readouterr()
assert '- a()' in captured.out
assert '+a()' in captured.out
assert captured.err == ''
def test_main_file_1(capsys, tmp_path):
testfile1 = tmp_path / 'list1.cmake'
with testfile1.open('w') as fp:
fp.write(' a()')
main([str(testfile1)])
captured = capsys.readouterr()
assert captured.out == 'a()\n'
assert captured.err == ''
def test_main_file_2(capsys, tmp_path):
testfile1 = tmp_path / 'list1.cmake'
with testfile1.open('w') as fp:
fp.write(' a()')
testfile2 = tmp_path / 'list2.cmake'
with testfile2.open('w') as fp:
fp.write(' b()')
main([str(testfile1), str(testfile2)])
captured = capsys.readouterr()
assert captured.out == 'a()\nb()\n'
assert captured.err == ''
def test_main_inplace(capsys, tmp_path):
testfile1 = tmp_path / 'list1.cmake'
with testfile1.open('w') as fp:
fp.write(' a()')
main(['-i', str(testfile1)])
captured = capsys.readouterr()
assert captured.out == ''
assert captured.err == ''
with testfile1.open() as fp:
content = fp.read()
assert content == 'a()\n'
def test_main_diff(capsys, tmp_path):
testfile1 = tmp_path / 'list1.cmake'
with testfile1.open('w') as fp:
fp.write(' a()')
main(['-d', str(testfile1)])
captured = capsys.readouterr()
assert str(testfile1) in captured.out
assert '- a()' in captured.out
assert '+a()' in captured.out
assert captured.err == ''

View File

@@ -1,64 +0,0 @@
from typing import List
from cmake_language_server.parser import ListParser, TokenType
def make_parser_test(liststr: str,
expect_token: List[TokenType],
expect_remain: str = ''):
def test():
actual_token, actual_remain = ListParser().parse(liststr)
assert actual_token == expect_token
assert actual_remain == expect_remain
return test
test_command_no_args = make_parser_test('a()', [('a', [])])
test_command_space = make_parser_test(' a ()', [' ', ('a', [])])
test_command_arg = make_parser_test('a(b)', [('a', ['b'])])
test_command_arg_space = make_parser_test('a ( b )', [('a', ['b'])])
test_command_arg_escape = make_parser_test(r'a(\n\")', [('a', [r'\n\"'])])
test_command_arg_paren = make_parser_test('a((b))', [('a', ['(', 'b', ')'])])
test_command_arg_paren_paren = make_parser_test(
'a(((b)))', [('a', ['(', '(', 'b', ')', ')'])])
test_command_arg_quote = make_parser_test(r'a("b\"")', [('a', [r'"b\""'])])
test_command_arg_quote_cont = make_parser_test('a("\\\n")',
[('a', ['"\\\n"'])])
test_command_arg_quo_multiline = make_parser_test('''a("b
c
")''', [('a', ['"b\nc\n"'])])
test_command_arg_bracket_0 = make_parser_test('a([[b]])', [('a', ['[[b]]'])])
test_command_arg_bracket_1 = make_parser_test('a([=[b]=])',
[('a', ['[=[b]=]'])])
test_command_arg_space = make_parser_test('a ( b )', [('a', [' ', 'b', ' '])])
test_command_arg_multi = make_parser_test('a(b c)', [('a', ['b', ' ', 'c'])])
test_command_multielement = make_parser_test('''a(
b
c # c
)''', [('a', ['\n', ' ', 'b', '\n', ' ', 'c', ' ', '# c', '\n'])])
test_line_comment = make_parser_test('a() # b # c',
[('a', []), ' ', '# b # c'])
test_bracket_comment = make_parser_test('#[[a]]#[[b]]', ['#[[a]]', '#[[b]]'])
test_bracket_comment_nested = make_parser_test('#[=[[[a]]]=]',
['#[=[[[a]]]=]'])
test_bracket_comment_multiline = make_parser_test('#[[\na\nb\nc\n]]',
['#[[\na\nb\nc\n]]'])
test_if_block = make_parser_test('''if()
a()
else()
b()
endif()''', [('if', []), '\n', ' ', ('a', []), '\n', ('else', []), '\n', ' ',
('b', []), '\n', ('endif', [])])
test_comment_multi_linecomment = make_parser_test(
'''a()# a
b() # b
c() # c''', [('a', []), '# a', '\n', ('b', []), ' ', '# b', '\n',
('c', []), ' ', '# c'])
test_incomplete_id = make_parser_test('a', [], 'a')
test_incomplete_command = make_parser_test('a(', [], 'a(')
test_incomplete_id_after_command = make_parser_test('a()\nb',
[('a', []), '\n'], 'b')
test_incomplete_command_after_command = make_parser_test(
'a()\nb(c', [('a', []), '\n'], 'b(c')

View File

@@ -1,35 +1,53 @@
from concurrent import futures from concurrent import futures
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Any, Dict, Optional, Tuple
from pygls.features import (COMPLETION, FORMATTING, HOVER, INITIALIZE, from cmake_language_server.server import CMakeLanguageServer
TEXT_DOCUMENT_DID_OPEN) from pygls.lsp.methods import (
COMPLETION,
FORMATTING,
HOVER,
INITIALIZE,
TEXT_DOCUMENT_DID_OPEN,
)
from pygls.lsp.types import (
ClientCapabilities,
CompletionContext,
CompletionParams,
CompletionTriggerKind,
DidOpenTextDocumentParams,
DocumentFormattingParams,
FormattingOptions,
InitializeParams,
Position,
TextDocumentIdentifier,
TextDocumentItem,
TextDocumentPositionParams,
)
from pygls.server import LanguageServer from pygls.server import LanguageServer
from pygls.types import (CompletionContext, CompletionParams,
CompletionTriggerKind, DidOpenTextDocumentParams,
DocumentFormattingParams, FormattingOptions,
InitializeParams, Position, TextDocumentIdentifier,
TextDocumentItem, TextDocumentPositionParams)
CALL_TIMEOUT = 2 CALL_TIMEOUT = 2
def _init(client: LanguageServer, root: Path): def _init(client: LanguageServer, root: Path) -> None:
retry = 3 retry = 3
while retry > 0: while retry > 0:
try: try:
client.lsp.send_request( client.lsp.send_request(
INITIALIZE, INITIALIZE,
InitializeParams( InitializeParams(
process_id=1234, root_uri=root.as_uri(), process_id=1234,
capabilities=None)).result(timeout=CALL_TIMEOUT) root_uri=root.as_uri(),
capabilities=ClientCapabilities(),
),
).result(timeout=CALL_TIMEOUT)
except futures.TimeoutError: except futures.TimeoutError:
retry -= 1 retry -= 1
else: else:
break break
def _open(client: LanguageServer, path: Path, text: Optional[str] = None): def _open(client: LanguageServer, path: Path, text: Optional[str] = None) -> None:
if text is None: if text is None:
with open(path) as fp: with open(path) as fp:
text = fp.read() text = fp.read()
@@ -37,10 +55,39 @@ def _open(client: LanguageServer, path: Path, text: Optional[str] = None):
client.lsp.notify( client.lsp.notify(
TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_DID_OPEN,
DidOpenTextDocumentParams( DidOpenTextDocumentParams(
TextDocumentItem(path.as_uri(), 'cmake', 1, text))) text_document=TextDocumentItem(
uri=path.as_uri(), language_id="cmake", version=1, text=text
)
),
)
def test_initialize(client_server, datadir): def _test_completion(
client_server: Tuple[LanguageServer, CMakeLanguageServer],
datadir: Path,
content: str,
context: Optional[CompletionContext],
) -> Dict[str, Any]:
client, server = client_server
_init(client, datadir)
path = datadir / "CMakeLists.txt"
_open(client, path, content)
params = CompletionParams(
text_document=TextDocumentIdentifier(uri=path.as_uri()),
position=Position(line=0, character=len(content)),
context=context,
)
if context is None:
# some clients do not send context
del params.context
ret = client.lsp.send_request(COMPLETION, params).result(timeout=CALL_TIMEOUT)
assert isinstance(ret, dict)
return ret
def test_initialize(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
client, server = client_server client, server = client_server
assert server._api is None assert server._api is None
@@ -48,83 +95,111 @@ def test_initialize(client_server, datadir):
assert server._api is not None assert server._api is not None
def test_completions_invoked(client_server, datadir): def test_completions_invoked(
client, server = client_server client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
_init(client, datadir) ) -> None:
path = datadir / 'CMakeLists.txt' response = _test_completion(
_open(client, path, 'projec') client_server,
response = client.lsp.send_request( datadir,
COMPLETION, "projec",
CompletionParams(TextDocumentIdentifier(path.as_uri()), Position( CompletionContext(trigger_kind=CompletionTriggerKind.Invoked),
0, 6), CompletionContext( )
CompletionTriggerKind.Invoked))).result(timeout=CALL_TIMEOUT) item = next(filter(lambda x: x["label"] == "project", response["items"]), None)
item = next(filter(lambda x: x.label == 'project', response.items), None)
assert item is not None assert item is not None
assert '<PROJECT-NAME>' in item.documentation assert isinstance(item["documentation"], str)
assert "<PROJECT-NAME>" in item["documentation"]
def test_completions_triggercharacter_variable(client_server, datadir): def test_completions_nocontext(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
response = _test_completion(client_server, datadir, "projec", None)
item = next(filter(lambda x: x["label"] == "project", response["items"]), None)
assert item is not None
assert isinstance(item["documentation"], str)
assert "<PROJECT-NAME>" in item["documentation"]
def test_completions_triggercharacter_variable(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
response = _test_completion(
client_server,
datadir,
"${",
CompletionContext(
trigger_kind=CompletionTriggerKind.TriggerCharacter, trigger_character="{"
),
)
assert "PROJECT_VERSION" in [x["label"] for x in response["items"]]
response_nocontext = _test_completion(client_server, datadir, "${", None)
assert response == response_nocontext
def test_completions_triggercharacter_module(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
response = _test_completion(
client_server,
datadir,
"include(",
CompletionContext(
trigger_kind=CompletionTriggerKind.TriggerCharacter, trigger_character="("
),
)
assert "GoogleTest" in [x["label"] for x in response["items"]]
response_nocontext = _test_completion(client_server, datadir, "include(", None)
assert response == response_nocontext
def test_completions_triggercharacter_package(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
response = _test_completion(
client_server,
datadir,
"find_package(",
CompletionContext(
trigger_kind=CompletionTriggerKind.TriggerCharacter, trigger_character="("
),
)
assert "Boost" in [x["label"] for x in response["items"]]
response_nocontext = _test_completion(client_server, datadir, "find_package(", None)
assert response == response_nocontext
def test_formatting(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
client, server = client_server client, server = client_server
_init(client, datadir) _init(client, datadir)
path = datadir / 'CMakeLists.txt' path = datadir / "CMakeLists.txt"
_open(client, path, '${') _open(client, path, "a ( b c ) ")
response = client.lsp.send_request(
COMPLETION,
CompletionParams(
TextDocumentIdentifier(path.as_uri()), Position(0, 2),
CompletionContext(CompletionTriggerKind.TriggerCharacter,
'{'))).result(timeout=CALL_TIMEOUT)
assert 'PROJECT_VERSION' in [x.label for x in response.items]
def test_completions_triggercharacter_module(client_server, datadir):
client, server = client_server
_init(client, datadir)
path = datadir / 'CMakeLists.txt'
_open(client, path, 'include(')
response = client.lsp.send_request(
COMPLETION,
CompletionParams(
TextDocumentIdentifier(path.as_uri()), Position(0, 8),
CompletionContext(CompletionTriggerKind.TriggerCharacter,
'('))).result(timeout=CALL_TIMEOUT)
assert 'GoogleTest' in [x.label for x in response.items]
def test_completions_triggercharacter_package(client_server, datadir):
client, server = client_server
_init(client, datadir)
path = datadir / 'CMakeLists.txt'
_open(client, path, 'find_package(')
response = client.lsp.send_request(
COMPLETION,
CompletionParams(
TextDocumentIdentifier(path.as_uri()), Position(0, 13),
CompletionContext(CompletionTriggerKind.TriggerCharacter,
'('))).result(timeout=CALL_TIMEOUT)
assert 'Boost' in [x.label for x in response.items]
def test_formatting(client_server, datadir):
client, server = client_server
_init(client, datadir)
path = datadir / 'CMakeLists.txt'
_open(client, path, 'a ( b c ) ')
response = client.lsp.send_request( response = client.lsp.send_request(
FORMATTING, FORMATTING,
DocumentFormattingParams(TextDocumentIdentifier(path.as_uri()), DocumentFormattingParams(
FormattingOptions( text_document=TextDocumentIdentifier(uri=path.as_uri()),
2, True))).result(timeout=CALL_TIMEOUT) options=FormattingOptions(tab_size=2, insert_spaces=True),
assert response[0].newText == 'a(b c)\n' ),
).result(timeout=CALL_TIMEOUT)
assert response[0]["newText"] == "a(b c)\n"
def test_hover(client_server, datadir): def test_hover(
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
) -> None:
client, server = client_server client, server = client_server
_init(client, datadir) _init(client, datadir)
path = datadir / 'CMakeLists.txt' path = datadir / "CMakeLists.txt"
_open(client, path, 'project()') _open(client, path, "project()")
response = client.lsp.send_request( response = client.lsp.send_request(
HOVER, HOVER,
TextDocumentPositionParams(TextDocumentIdentifier(path.as_uri()), TextDocumentPositionParams(
Position())).result(timeout=CALL_TIMEOUT) text_document=TextDocumentIdentifier(uri=path.as_uri()),
assert '<PROJECT-NAME>' in response.contents.value position=Position(line=0, character=0),
),
).result(timeout=CALL_TIMEOUT)
assert "<PROJECT-NAME>" in response["contents"]["value"]

33
tox.ini
View File

@@ -1,26 +1,37 @@
[tox] [tox]
isolated_build = True isolated_build = True
skipsdist = True skipsdist = True
envlist = py36, py37, py38, lint envlist = py{36,37,38,39,310}-{linux,windows}
[gh-actions] [gh-actions]
python = python =
3.6: py36 3.7: py37
3.7: py37, lint
3.8: py38 3.8: py38
3.9: py39
3.10: py310
[gh-actions:env]
os =
ubuntu-18.04: linux
windows-2019: windows
[testenv] [testenv]
whitelist_externals = poetry allowlist_externals =
poetry
git
skip_install = true skip_install = true
passenv = INCLUDE LIB LIBPATH Platform VCTools* VSCMD_* WindowsSDK* passenv = *
commands_pre = commands_pre =
poetry install poetry install
commands = commands =
poetry run pytest --cov-report=term --cov-report=xml --cov=src -sv tests pytest --cov-report=term --cov-report=xml --cov=src -sv tests
[testenv:lint] pip install "pysen[lint]"
pip install "black>=22.3.0"
pysen run format
git diff --exit-code --ignore-submodules
pysen run lint
[testenv:py310-windows]
commands = commands =
poetry run isort -c -rc src tests pytest --cov-report=term --cov-report=xml --cov=src -sv tests
poetry run yapf -d -r src tests
poetry run flake8 src tests
poetry run mypy src tests