Compare commits
90 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
46bc1aaa84
|
|||
|
|
3e5e86736c | ||
|
|
806d44f424 | ||
|
|
3760f15580 | ||
|
|
1c40387cac | ||
|
|
890bb77f8a | ||
|
|
9529df5d68 | ||
|
|
8cc05adcf4 | ||
|
|
68bbc8187b | ||
|
|
0916b888d8 | ||
|
|
cd7ba40b55 | ||
|
|
7b68a4e0fe | ||
|
|
60c376a5fd | ||
|
|
a28d3803ad | ||
|
|
691beef1dc | ||
|
|
2a5983f9aa | ||
|
|
bff7990e7d | ||
|
|
981150e308 | ||
|
|
6de2cc3867 | ||
|
|
1fefcb4cba | ||
|
|
f0cfa8b13f | ||
|
|
722dae419e | ||
|
|
6a3140f105 | ||
|
|
4dec2f5afa | ||
|
|
9f7754f0e6 | ||
|
|
9630b96935 | ||
|
|
76e34ae628 | ||
|
|
cbb6bdd1ae | ||
|
|
a5af5b505f | ||
|
|
4d120a6a98 | ||
|
|
6e839f7675 | ||
|
|
cade1e2c45 | ||
|
|
d16d3b24ef | ||
|
|
4be7657edb | ||
|
|
040f0b9f0c | ||
|
|
ef2c31c6a3 | ||
|
|
87879ee5df | ||
|
|
01b1fac73e | ||
|
|
5550cb259c | ||
|
|
466c5b7bcc | ||
|
|
0ec120f391 | ||
|
|
5d916b6989 | ||
|
|
1c606ee8a8 | ||
|
|
cdb62adce3 | ||
|
|
6ac3b1d17f | ||
|
|
ce2c3a21db | ||
|
|
3697fae2d3 | ||
|
|
48d5980a36 | ||
|
|
e07b3242c8 | ||
|
|
2d36887b26 | ||
|
|
67aced6544 | ||
|
|
3c171b9e25 | ||
|
|
c8c284e061 | ||
|
|
6bf08e0f14 | ||
|
|
40d93525d9 | ||
|
|
f8136d6dbc | ||
|
|
5af6555d3c | ||
|
|
310c449250 | ||
|
|
bd4f357e59 | ||
|
|
02ae7c4a7c | ||
|
|
6e03b145ba | ||
|
|
b3123db464 | ||
|
|
7d85c2c8dd | ||
|
|
b08dc91e53 | ||
|
|
894b38d55d | ||
|
|
cc9877adbe | ||
|
|
66af586b2a | ||
|
|
c870e3d512 | ||
|
|
9e6fc1a277 | ||
|
|
79864d24ee | ||
|
|
6f93218462 | ||
|
|
600659fe81 | ||
|
|
af59e9b3f6 | ||
|
|
39aa03cd55 | ||
|
|
8d5c6b588c | ||
|
|
b2d8e66ef1 | ||
|
|
4624bdf4e8 | ||
|
|
6628dfe5d0 | ||
|
|
e73b0bab0f | ||
|
|
06f7a4669d | ||
|
|
1a8267bb74 | ||
|
|
9670ecfb59 | ||
|
|
3b8c225d06 | ||
|
|
ff727b7793 | ||
|
|
1ac3bf421d | ||
|
|
7919cf5025 | ||
|
|
b4dd6c840d | ||
|
|
0021c07b56 | ||
|
|
5e2736a710 | ||
|
|
f89f0d6b56 |
12
.editorconfig
Normal file
12
.editorconfig
Normal 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
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
poetry.lock linguist-generated=true
|
||||
26
.github/workflows/publish.yml
vendored
26
.github/workflows/publish.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
build-n-publish:
|
||||
name: Build and publish
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install poetry
|
||||
- name: Build a binary wheel and a source tarball
|
||||
run: |
|
||||
poetry build
|
||||
- name: Publish distribution to PyPI
|
||||
run: |
|
||||
poetry publish -u __token__ -p ${{ secrets.pypi_password }}
|
||||
35
.github/workflows/tests.yml
vendored
35
.github/workflows/tests.yml
vendored
@@ -1,35 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
env:
|
||||
CMAKE_VERSION: 3.14.7
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04]
|
||||
python: [3.6, 3.7, 3.8]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Cache .tox
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: .tox
|
||||
key: ${{ runner.OS }}-tox-${{ hashFiles('poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-tox-
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
curl -sSL https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz | tar xz
|
||||
python -m pip install --upgrade setuptools pip wheel
|
||||
python -m pip install poetry tox-gh-actions
|
||||
- name: Test with tox
|
||||
run: |
|
||||
export PATH=$GITHUB_WORKSPACE/cmake-$CMAKE_VERSION-Linux-x86_64/bin:$PATH
|
||||
tox
|
||||
133
.gitignore
vendored
133
.gitignore
vendored
@@ -1,133 +1,18 @@
|
||||
### https://raw.github.com/github/gitignore/cb0c6ef7ac68f2300409ee85501d9ad432cb4c7e/Python.gitignore
|
||||
cmake_language_server/version.py
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
.venv/
|
||||
pyrightconfig.json
|
||||
|
||||
/build/
|
||||
/dist/
|
||||
__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/
|
||||
.nox/
|
||||
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.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
|
||||
.pdm-python
|
||||
__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/
|
||||
|
||||
|
||||
.pdm-build/
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[style]
|
||||
based_on_style = pep8
|
||||
43
README.md
43
README.md
@@ -1,5 +1,9 @@
|
||||
# cmake-language-server
|
||||
[](https://pypi.org/project/cmake-language-server)
|
||||
[](https://aur.archlinux.org/packages/cmake-language-server/)
|
||||
[](https://github.com/regen100/cmake-language-server/actions)
|
||||
[](https://codecov.io/gh/regen100/cmake-language-server)
|
||||
[](https://github.com/regen100/cmake-language-server/blob/master/LICENSE)
|
||||
|
||||
CMake LSP Implementation.
|
||||
|
||||
@@ -8,21 +12,26 @@ Alpha Stage, work in progress.
|
||||
## Features
|
||||
- [x] Builtin command completion
|
||||
- [x] Documentation for commands and variables on hover
|
||||
- [x] Formatting
|
||||
- [x] Formatting (by [`cmake-format`](https://github.com/cheshirekow/cmake_format))
|
||||
|
||||
## Commands
|
||||
|
||||
- cmake-language-server: LSP server
|
||||
- cmake-format: CLI frontend for formatting
|
||||
- `cmake-language-server`: LSP server
|
||||
|
||||
## Installation
|
||||
|
||||
### Clients
|
||||
```bash
|
||||
$ pip install cmake-language-server
|
||||
```
|
||||
|
||||
- Neovim ([neoclide/coc.nvim][coc.nvim])
|
||||
### Tested Clients
|
||||
|
||||
- Neovim ([neoclide/coc.nvim][coc.nvim], [prabirshrestha/vim-lsp][vim-lsp])
|
||||
|
||||
#### Neovim
|
||||
|
||||
##### coc.nvim
|
||||
|
||||
```jsonc
|
||||
"languageserver": {
|
||||
"cmake": {
|
||||
@@ -38,5 +47,29 @@ Alpha Stage, work in progress.
|
||||
}
|
||||
```
|
||||
|
||||
##### 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
|
||||
[vim-lsp]: https://github.com/prabirshrestha/vim-lsp
|
||||
|
||||
3
cmake_language_server/__init__.py
Normal file
3
cmake_language_server/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .version import __version__
|
||||
|
||||
__all__ = ["__version__"]
|
||||
365
cmake_language_server/api.py
Normal file
365
cmake_language_server/api.py
Normal file
@@ -0,0 +1,365 @@
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Pattern
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _tidy_doc(doc: str) -> str:
|
||||
doc = doc.strip()
|
||||
doc = re.sub(r":.+?:`(.+?)`", r"\1", doc)
|
||||
doc = re.sub(r"``([^`]+)``", r"`\1`", doc)
|
||||
doc = doc.replace("\n", " ")
|
||||
doc = doc.replace(". ", ". ")
|
||||
return doc
|
||||
|
||||
|
||||
class API(object):
|
||||
_cmake: str
|
||||
_build: Path
|
||||
_uuid: uuid.UUID
|
||||
_builtin_commands: Dict[str, str]
|
||||
_builtin_variables: Dict[str, str]
|
||||
_builtin_variable_template: Dict[Pattern[str], str]
|
||||
_builtin_modules: Dict[str, str]
|
||||
_targets: List[str]
|
||||
_cached_variables: Dict[str, str]
|
||||
_generated_list_parsed: bool
|
||||
|
||||
def __init__(self, cmake: str, build: Path):
|
||||
self._cmake = cmake
|
||||
self._build = Path(build)
|
||||
self._uuid = uuid.uuid4()
|
||||
|
||||
self._builtin_commands = {}
|
||||
self._builtin_variables = {}
|
||||
self._builtin_variable_template = {}
|
||||
self._builtin_modules = {}
|
||||
self._targets = []
|
||||
self._cached_variables = {}
|
||||
self._generated_list_parsed = False
|
||||
|
||||
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():
|
||||
return False
|
||||
|
||||
self.query_json.parent.mkdir(parents=True, exist_ok=True)
|
||||
with self.query_json.open("w") as fp:
|
||||
fp.write(
|
||||
"""\
|
||||
{
|
||||
"requests": [
|
||||
{"kind": "codemodel", "version": 2},
|
||||
{"kind": "cache", "version": 2},
|
||||
{"kind": "cmakeFiles", "version": 1}
|
||||
]
|
||||
}"""
|
||||
)
|
||||
|
||||
proc = subprocess.run(
|
||||
[self._cmake, str(self._build)],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
encoding="utf-8",
|
||||
universal_newlines=True,
|
||||
)
|
||||
self.query_json.unlink()
|
||||
self.query_json.parent.rmdir()
|
||||
if proc.returncode != 0:
|
||||
logging.error(f"cmake exited with {proc.returncode}: {proc.stderr}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def read_reply(self) -> bool:
|
||||
"""Reads the CMake file API reply file and updates internal state
|
||||
|
||||
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:
|
||||
logger.error("no reply")
|
||||
return False
|
||||
with indices[-1].open() as fp:
|
||||
index = json.load(fp)
|
||||
try:
|
||||
responses = index["reply"][f"client-{self._uuid}"]["query.json"][
|
||||
"responses"
|
||||
]
|
||||
except KeyError:
|
||||
logger.error("no rensponse")
|
||||
return False
|
||||
for response in responses:
|
||||
if response["kind"] == "codemodel":
|
||||
self._read_codemodel(reply / response["jsonFile"])
|
||||
elif response["kind"] == "cache":
|
||||
self._read_cache(reply / response["jsonFile"])
|
||||
elif response["kind"] == "cmakeFiles":
|
||||
self._read_cmake_files(reply / response["jsonFile"])
|
||||
|
||||
return True
|
||||
|
||||
def _read_codemodel(self, codemodelpath: Path) -> None:
|
||||
with (codemodelpath).open() as fp:
|
||||
codemodel = json.load(fp)
|
||||
config = codemodel["configurations"][0]
|
||||
self._targets[:] = [x["name"] for x in config["targets"]]
|
||||
|
||||
def _read_cache(self, cachepath: Path) -> None:
|
||||
with cachepath.open() as fp:
|
||||
cache = json.load(fp)
|
||||
self._cached_variables.clear()
|
||||
for entry in cache["entries"]:
|
||||
name = entry["name"]
|
||||
value = self._truncate_variable(entry["value"])
|
||||
properties = {x["name"]: x["value"] for x in entry["properties"]}
|
||||
helpstring = properties.get("HELPSTRING", "")
|
||||
doc = []
|
||||
if helpstring:
|
||||
doc.append(helpstring)
|
||||
if value:
|
||||
doc.append(f"`{value}`")
|
||||
self._cached_variables[name] = "\n\n".join(doc)
|
||||
|
||||
def _read_cmake_files(self, jsonpath: Path) -> None:
|
||||
"""inspect CMake list files that are used during build generation"""
|
||||
|
||||
if not self._builtin_variables or self._generated_list_parsed:
|
||||
return
|
||||
|
||||
with jsonpath.open() as fp:
|
||||
cmake_files = json.load(fp)
|
||||
|
||||
# Inspect generated list files: Get the values of variables in each script
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
tmplist = Path(tmpdirname) / "dump.cmake"
|
||||
with tmplist.open("w") as fp:
|
||||
for listfile in cmake_files["inputs"]:
|
||||
if not listfile.get("isGenerated", False):
|
||||
continue
|
||||
path = listfile["path"]
|
||||
fp.write(f"include({path})\n")
|
||||
fp.write(
|
||||
"""
|
||||
get_cmake_property(variables VARIABLES)
|
||||
foreach (variable ${variables})
|
||||
message("${variable}=${${variable}}")
|
||||
endforeach()
|
||||
"""
|
||||
)
|
||||
p = subprocess.run(
|
||||
[self._cmake, "-P", str(tmplist)],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cmake_files["paths"]["source"],
|
||||
encoding="utf-8",
|
||||
universal_newlines=True,
|
||||
)
|
||||
if p.returncode != 0:
|
||||
return
|
||||
|
||||
for line in p.stderr.split("\n"):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
k, v = line.split("=", 1)
|
||||
if k.startswith("CMAKE_ARG"):
|
||||
continue
|
||||
v = self._truncate_variable(v)
|
||||
if k in self._builtin_variables:
|
||||
self._builtin_variables[k] += f"\n\n`{v}`"
|
||||
else:
|
||||
for pattern, doc in self._builtin_variable_template.items():
|
||||
if pattern.fullmatch(k):
|
||||
self._builtin_variables[k] = f"{doc}\n\n`{v}`"
|
||||
break
|
||||
else:
|
||||
# ignore variable with no document
|
||||
pass
|
||||
|
||||
self._generated_list_parsed = True
|
||||
|
||||
@property
|
||||
def query_json(self) -> Path:
|
||||
return (
|
||||
self._build
|
||||
/ ".cmake"
|
||||
/ "api"
|
||||
/ "v1"
|
||||
/ "query"
|
||||
/ f"client-{self._uuid}"
|
||||
/ "query.json"
|
||||
)
|
||||
|
||||
@property
|
||||
def cmake_cache(self) -> Path:
|
||||
return self._build / "CMakeCache.txt"
|
||||
|
||||
def parse_doc(self) -> None:
|
||||
self._parse_commands()
|
||||
self._parse_variables()
|
||||
self._parse_modules()
|
||||
|
||||
def _parse_commands(self) -> None:
|
||||
"""Load docs for builtin cmake functions
|
||||
|
||||
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:
|
||||
return
|
||||
|
||||
matches = re.finditer(
|
||||
r"""
|
||||
(?P<command>.+)\n
|
||||
-+\n+?
|
||||
[\s\S]*?
|
||||
(?P<signature>(?P=command)\s*\([^)]*\))
|
||||
""",
|
||||
p.stdout,
|
||||
re.VERBOSE,
|
||||
)
|
||||
self._builtin_commands.clear()
|
||||
for match in matches:
|
||||
command = match.group("command")
|
||||
signature = match.group("signature")
|
||||
signature = re.sub(r"^ ", r"", signature, flags=re.MULTILINE)
|
||||
self._builtin_commands[command] = "```cmake\n" + signature + "\n```"
|
||||
|
||||
def _parse_variables(self) -> None:
|
||||
"""Load docs for builtin cmake variables
|
||||
|
||||
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:
|
||||
return
|
||||
|
||||
matches = re.finditer(
|
||||
r"""
|
||||
(?P<variable>.+)\n
|
||||
-+\n\n
|
||||
(?P<doc>[\s\S]+?)(?:\n\n|$)
|
||||
""",
|
||||
p.stdout,
|
||||
re.VERBOSE,
|
||||
)
|
||||
self._builtin_variables.clear()
|
||||
for match in matches:
|
||||
variable = match.group("variable")
|
||||
doc = _tidy_doc(match.group("doc"))
|
||||
if variable == "CMAKE_MATCH_<n>":
|
||||
for i in range(10):
|
||||
self._builtin_variables[f"CMAKE_MATCH_{i}"] = doc
|
||||
elif "<" in variable:
|
||||
variable = re.sub(r"<[^>]+>", r"[^_]+", variable)
|
||||
pattern = re.compile(variable)
|
||||
self._builtin_variable_template[pattern] = doc
|
||||
else:
|
||||
self._builtin_variables[variable] = doc
|
||||
|
||||
def _parse_modules(self) -> None:
|
||||
"""Loads docs for all modules in the cmake distribution
|
||||
|
||||
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:
|
||||
return
|
||||
|
||||
matches = re.finditer(
|
||||
r"""
|
||||
(?P<module>.+)\n
|
||||
-+\n+?
|
||||
(?:(?P<header>\w[\w\s]+)\n\^+\n+?)?
|
||||
(?P<doc>(?:.|\n)*?\n\n)
|
||||
""",
|
||||
p.stdout + "\n\n",
|
||||
re.VERBOSE,
|
||||
)
|
||||
self._builtin_modules.clear()
|
||||
for match in matches:
|
||||
module = match.group("module")
|
||||
header = match.group("header")
|
||||
doc = _tidy_doc(match.group("doc"))
|
||||
if header != "Overview":
|
||||
doc = ""
|
||||
self._builtin_modules[module] = doc
|
||||
|
||||
def get_command_doc(self, command: str) -> Optional[str]:
|
||||
return self._builtin_commands.get(command)
|
||||
|
||||
def search_command(self, command: str) -> List[str]:
|
||||
command = command.lower()
|
||||
return [x for x in self._builtin_commands if x.startswith(command)]
|
||||
|
||||
def get_variable_doc(self, variable: str) -> Optional[str]:
|
||||
doc = self._cached_variables.get(variable)
|
||||
if doc:
|
||||
return doc
|
||||
return self._builtin_variables.get(variable)
|
||||
|
||||
def search_variable(self, variable: str) -> List[str]:
|
||||
cached = frozenset(x for x in self._cached_variables if x.startswith(variable))
|
||||
builtin = frozenset(
|
||||
x for x in self._builtin_variables if x.startswith(variable)
|
||||
)
|
||||
return list(cached | builtin)
|
||||
|
||||
def get_module_doc(self, module: str, package: bool) -> Optional[str]:
|
||||
if package:
|
||||
return self._builtin_modules.get("Find" + module)
|
||||
|
||||
return self._builtin_modules.get(module)
|
||||
|
||||
def search_module(self, module: str, package: bool) -> List[str]:
|
||||
if package:
|
||||
module = "Find" + module
|
||||
return [x[4:] for x in self._builtin_modules if x.startswith(module)]
|
||||
|
||||
return [
|
||||
x
|
||||
for x in self._builtin_modules
|
||||
if x.startswith(module) and not x.startswith("Find")
|
||||
]
|
||||
|
||||
def search_target(self, target: str) -> List[str]:
|
||||
return [x for x in self._targets if x.startswith(target)]
|
||||
|
||||
def _truncate_variable(self, v: str) -> str:
|
||||
width = 70
|
||||
return v[:width] + (v[width:] and "...")
|
||||
287
cmake_language_server/server.py
Normal file
287
cmake_language_server/server.py
Normal file
@@ -0,0 +1,287 @@
|
||||
import logging
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, List, Optional, Tuple
|
||||
|
||||
from lsprotocol.types import (
|
||||
INITIALIZE,
|
||||
INITIALIZED,
|
||||
TEXT_DOCUMENT_COMPLETION,
|
||||
TEXT_DOCUMENT_DID_SAVE,
|
||||
TEXT_DOCUMENT_FORMATTING,
|
||||
TEXT_DOCUMENT_HOVER,
|
||||
WORKSPACE_DID_CHANGE_CONFIGURATION,
|
||||
CompletionItem,
|
||||
CompletionItemKind,
|
||||
CompletionList,
|
||||
CompletionOptions,
|
||||
CompletionParams,
|
||||
CompletionTriggerKind,
|
||||
DidChangeConfigurationParams,
|
||||
DocumentFormattingParams,
|
||||
Hover,
|
||||
InitializeParams,
|
||||
MarkupContent,
|
||||
MarkupKind,
|
||||
Position,
|
||||
Range,
|
||||
SaveOptions,
|
||||
TextDocumentPositionParams,
|
||||
TextEdit,
|
||||
)
|
||||
from pygls.lsp.server import LanguageServer
|
||||
|
||||
from .api import API
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CMakeLanguageServer(LanguageServer):
|
||||
_api: Optional[API]
|
||||
|
||||
def __init__(self, *args: Any) -> None:
|
||||
super().__init__(*args)
|
||||
|
||||
self._api = None
|
||||
|
||||
@self.feature(INITIALIZE)
|
||||
def initialize(params: InitializeParams) -> None:
|
||||
opts = params.initialization_options or {}
|
||||
|
||||
cmake = opts.get("cmakeExecutable", "cmake")
|
||||
builddir = opts.get("buildDirectory", "")
|
||||
logging.info(f"cmakeExecutable={cmake}, buildDirectory={builddir}")
|
||||
|
||||
self._api = API(cmake, Path(builddir))
|
||||
self._api.parse_doc()
|
||||
|
||||
@self.feature(WORKSPACE_DID_CHANGE_CONFIGURATION)
|
||||
def workspace_did_change_configuration(
|
||||
params: DidChangeConfigurationParams,
|
||||
) -> None:
|
||||
settings = params.settings or {}
|
||||
assert self._api is not None
|
||||
if opts := settings.get("initialization_options"):
|
||||
cmake = opts.get("cmakeExecutable", self._api._cmake)
|
||||
builddir = opts.get("buildDirectory", self._api._build.as_posix())
|
||||
logging.info(f"cmakeExecutable={cmake}, buildDirectory={builddir}")
|
||||
|
||||
api = API(cmake, Path(builddir))
|
||||
api.parse_doc()
|
||||
|
||||
self._api = api
|
||||
|
||||
run_cmake()
|
||||
|
||||
trigger_characters = ["{", "("]
|
||||
|
||||
@self.feature(
|
||||
TEXT_DOCUMENT_COMPLETION,
|
||||
CompletionOptions(trigger_characters=trigger_characters),
|
||||
)
|
||||
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:
|
||||
line = self._cursor_line(params.text_document.uri, params.position)
|
||||
idx = params.position.character - 1
|
||||
if 0 <= idx < len(line) and line[idx] in trigger_characters:
|
||||
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] = []
|
||||
|
||||
if trigger is None:
|
||||
commands = self._api.search_command(token)
|
||||
items.extend(
|
||||
CompletionItem(
|
||||
label=x,
|
||||
kind=CompletionItemKind.Function,
|
||||
documentation=self._get_command_doc(x),
|
||||
insert_text=x,
|
||||
)
|
||||
for x in commands
|
||||
)
|
||||
|
||||
if trigger is None or trigger == "{":
|
||||
variables = self._api.search_variable(token)
|
||||
items.extend(
|
||||
CompletionItem(
|
||||
label=x,
|
||||
kind=CompletionItemKind.Variable,
|
||||
documentation=self._get_variable_doc(x),
|
||||
insert_text=x,
|
||||
)
|
||||
for x in variables
|
||||
)
|
||||
|
||||
if trigger is None:
|
||||
targets = self._api.search_target(token)
|
||||
items.extend(
|
||||
CompletionItem(
|
||||
label=x, kind=CompletionItemKind.Class, insert_text=x
|
||||
)
|
||||
for x in targets
|
||||
)
|
||||
|
||||
if trigger == "(":
|
||||
func = self._cursor_function(params.text_document.uri, params.position)
|
||||
if func is not None:
|
||||
func = func.lower()
|
||||
if func == "include":
|
||||
modules = self._api.search_module(token, False)
|
||||
items.extend(
|
||||
CompletionItem(
|
||||
label=x,
|
||||
kind=CompletionItemKind.Module,
|
||||
documentation=self._get_module_doc(x, False),
|
||||
insert_text=x,
|
||||
)
|
||||
for x in modules
|
||||
)
|
||||
elif func == "find_package":
|
||||
modules = self._api.search_module(token, True)
|
||||
items.extend(
|
||||
CompletionItem(
|
||||
label=x,
|
||||
kind=CompletionItemKind.Module,
|
||||
documentation=self._get_module_doc(x, True),
|
||||
insert_text=x,
|
||||
)
|
||||
for x in modules
|
||||
)
|
||||
|
||||
return CompletionList(is_incomplete=False, items=items)
|
||||
|
||||
if shutil.which("cmake-format") is not None:
|
||||
|
||||
@self.feature(TEXT_DOCUMENT_FORMATTING)
|
||||
def formatting(
|
||||
params: DocumentFormattingParams,
|
||||
) -> Optional[List[TextEdit]]:
|
||||
doc = self.workspace.get_text_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(TEXT_DOCUMENT_HOVER)
|
||||
def hover(params: TextDocumentPositionParams) -> Optional[Hover]:
|
||||
word = self._cursor_word(params.text_document.uri, params.position, True)
|
||||
if not word:
|
||||
return None
|
||||
|
||||
candidates: List[Callable[[str], Optional[MarkupContent]]] = [
|
||||
lambda x: self._get_command_doc(x.lower()),
|
||||
lambda x: self._get_variable_doc(x),
|
||||
lambda x: self._get_module_doc(x, False),
|
||||
lambda x: self._get_module_doc(x, True),
|
||||
]
|
||||
for c in candidates:
|
||||
doc = c(word[0])
|
||||
if doc is None:
|
||||
continue
|
||||
return Hover(contents=doc, range=word[1])
|
||||
return None
|
||||
|
||||
@self.thread()
|
||||
@self.feature(
|
||||
TEXT_DOCUMENT_DID_SAVE,
|
||||
SaveOptions(include_text=False),
|
||||
)
|
||||
@self.feature(INITIALIZED)
|
||||
def run_cmake(*args: Any) -> None:
|
||||
assert self._api is not None
|
||||
|
||||
if self._api.query():
|
||||
self._api.read_reply()
|
||||
|
||||
def _cursor_function(self, uri: str, position: Position) -> Optional[str]:
|
||||
doc = self.workspace.get_text_document(uri)
|
||||
lines = doc.source.split("\n")[: position.line + 1]
|
||||
lines[-1] = lines[-1][: position.character - 1].strip()
|
||||
words = re.split(r"[\s\n()]+", "\n".join(lines))
|
||||
return words[-1] if words else None
|
||||
|
||||
def _cursor_line(self, uri: str, position: Position) -> str:
|
||||
doc = self.workspace.get_text_document(uri)
|
||||
content = doc.source
|
||||
line = content.split("\n")[position.line]
|
||||
return str(line)
|
||||
|
||||
def _cursor_word(
|
||||
self, uri: str, position: Position, include_all: bool = True
|
||||
) -> Optional[Tuple[str, Range]]:
|
||||
line = self._cursor_line(uri, position)
|
||||
cursor = position.character
|
||||
for m in re.finditer(r"\w+", line):
|
||||
end = m.end() if include_all else cursor
|
||||
if m.start() <= cursor <= m.end():
|
||||
word = (
|
||||
line[m.start() : end],
|
||||
Range(
|
||||
start=Position(line=position.line, character=m.start()),
|
||||
end=Position(line=position.line, character=end),
|
||||
),
|
||||
)
|
||||
return word
|
||||
return None
|
||||
|
||||
def _get_command_doc(self, command: str) -> Optional[MarkupContent]:
|
||||
assert self._api is not None
|
||||
docs = self._api.get_command_doc(command)
|
||||
return None if docs is None else MarkupContent(MarkupKind.Markdown, docs)
|
||||
|
||||
def _get_variable_doc(self, variable: str) -> Optional[MarkupContent]:
|
||||
assert self._api is not None
|
||||
docs = self._api.get_variable_doc(variable)
|
||||
return None if docs is None else MarkupContent(MarkupKind.Markdown, docs)
|
||||
|
||||
def _get_module_doc(self, module: str, package: bool) -> Optional[MarkupContent]:
|
||||
assert self._api is not None
|
||||
docs = self._api.get_module_doc(module, package)
|
||||
return None if docs is None else MarkupContent(MarkupKind.Markdown, docs)
|
||||
|
||||
|
||||
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.getLogger("pygls").setLevel(logging.WARNING)
|
||||
CMakeLanguageServer("cmake-language-server", __version__).start_io()
|
||||
7
codecov.yml
Normal file
7
codecov.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 10%
|
||||
patch:
|
||||
default: off
|
||||
6
mypy.ini
6
mypy.ini
@@ -1,6 +0,0 @@
|
||||
[mypy]
|
||||
ignore_missing_imports = True
|
||||
allow_redefinition = True
|
||||
|
||||
[mypy-re.Scanner]
|
||||
ignore_errors = True
|
||||
504
pdm.lock
generated
Normal file
504
pdm.lock
generated
Normal file
@@ -0,0 +1,504 @@
|
||||
# This file is @generated by PDM.
|
||||
# It is not intended for manual editing.
|
||||
|
||||
[metadata]
|
||||
groups = ["default", "dev", "lint"]
|
||||
strategy = ["cross_platform"]
|
||||
lock_version = "4.5.0"
|
||||
content_hash = "sha256:ef24aee91e46f0db41b85228c16cb2703f20aff76eb93225d2a982304959120e"
|
||||
|
||||
[[metadata.targets]]
|
||||
requires_python = ">=3.10.0,<3.16"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "25.4.0"
|
||||
requires_python = ">=3.9"
|
||||
summary = "Classes Without Boilerplate"
|
||||
files = [
|
||||
{file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"},
|
||||
{file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.3.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "The uncompromising code formatter."
|
||||
dependencies = [
|
||||
"click>=8.0.0",
|
||||
"mypy-extensions>=0.4.3",
|
||||
"packaging>=22.0",
|
||||
"pathspec>=0.9.0",
|
||||
"platformdirs>=2",
|
||||
"tomli>=1.1.0; python_version < \"3.11\"",
|
||||
"typing-extensions>=4.0.1; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"},
|
||||
{file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"},
|
||||
{file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"},
|
||||
{file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"},
|
||||
{file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"},
|
||||
{file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"},
|
||||
{file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"},
|
||||
{file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"},
|
||||
{file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"},
|
||||
{file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"},
|
||||
{file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"},
|
||||
{file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"},
|
||||
{file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"},
|
||||
{file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"},
|
||||
{file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"},
|
||||
{file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"},
|
||||
{file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"},
|
||||
{file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"},
|
||||
{file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"},
|
||||
{file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"},
|
||||
{file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"},
|
||||
{file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cattrs"
|
||||
version = "23.2.3"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Composable complex class support for attrs and dataclasses."
|
||||
dependencies = [
|
||||
"attrs>=23.1.0",
|
||||
"exceptiongroup>=1.1.1; python_version < \"3.11\"",
|
||||
"typing-extensions!=4.6.3,>=4.1.0; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"},
|
||||
{file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.7"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Composable command line interface toolkit"
|
||||
dependencies = [
|
||||
"colorama; platform_system == \"Windows\"",
|
||||
]
|
||||
files = [
|
||||
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
|
||||
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmakelang"
|
||||
version = "0.6.13"
|
||||
summary = "Language tools for cmake (format, lint, etc)"
|
||||
dependencies = [
|
||||
"six>=1.13.0",
|
||||
]
|
||||
files = [
|
||||
{file = "cmakelang-0.6.13-py3-none-any.whl", hash = "sha256:764b9467195c7c36453d60a829f30229720d26c7dffd41cb516b99bd9c7daf4e"},
|
||||
{file = "cmakelang-0.6.13.tar.gz", hash = "sha256:03982e87b00654d024d73ef972d9d9bb0e5726cdb6b8a424a15661fb6278e67f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
summary = "Cross-platform colored terminal text."
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.4.4"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Code coverage measurement for Python"
|
||||
files = [
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
|
||||
{file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
|
||||
{file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.4.4"
|
||||
extras = ["toml"]
|
||||
requires_python = ">=3.8"
|
||||
summary = "Code coverage measurement for Python"
|
||||
dependencies = [
|
||||
"coverage==7.4.4",
|
||||
"tomli; python_full_version <= \"3.11.0a6\"",
|
||||
]
|
||||
files = [
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
|
||||
{file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
|
||||
{file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.2.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Backport of PEP 654 (exception groups)"
|
||||
files = [
|
||||
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
|
||||
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "5.0.4"
|
||||
requires_python = ">=3.6.1"
|
||||
summary = "the modular source code checker: pep8 pyflakes and co"
|
||||
dependencies = [
|
||||
"mccabe<0.8.0,>=0.7.0",
|
||||
"pycodestyle<2.10.0,>=2.9.0",
|
||||
"pyflakes<2.6.0,>=2.5.0",
|
||||
]
|
||||
files = [
|
||||
{file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
|
||||
{file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "brain-dead simple config-ini parsing"
|
||||
files = [
|
||||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.13.2"
|
||||
requires_python = ">=3.8.0"
|
||||
summary = "A Python utility / library to sort Python imports."
|
||||
files = [
|
||||
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
|
||||
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lsprotocol"
|
||||
version = "2025.0.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Python types for Language Server Protocol."
|
||||
dependencies = [
|
||||
"attrs>=21.3.0",
|
||||
"cattrs!=23.2.1",
|
||||
]
|
||||
files = [
|
||||
{file = "lsprotocol-2025.0.0-py3-none-any.whl", hash = "sha256:f9d78f25221f2a60eaa4a96d3b4ffae011b107537facee61d3da3313880995c7"},
|
||||
{file = "lsprotocol-2025.0.0.tar.gz", hash = "sha256:e879da2b9301e82cfc3e60d805630487ac2f7ab17492f4f5ba5aaba94fe56c29"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.7.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "McCabe checker, plugin for flake8"
|
||||
files = [
|
||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.9.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Optional static typing for Python"
|
||||
dependencies = [
|
||||
"mypy-extensions>=1.0.0",
|
||||
"tomli>=1.1.0; python_version < \"3.11\"",
|
||||
"typing-extensions>=4.1.0",
|
||||
]
|
||||
files = [
|
||||
{file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"},
|
||||
{file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"},
|
||||
{file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"},
|
||||
{file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"},
|
||||
{file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"},
|
||||
{file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"},
|
||||
{file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"},
|
||||
{file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"},
|
||||
{file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"},
|
||||
{file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"},
|
||||
{file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"},
|
||||
{file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"},
|
||||
{file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"},
|
||||
{file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"},
|
||||
{file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"},
|
||||
{file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"},
|
||||
{file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"},
|
||||
{file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"},
|
||||
{file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"},
|
||||
{file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"},
|
||||
{file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"},
|
||||
{file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"},
|
||||
{file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"},
|
||||
{file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"},
|
||||
{file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"},
|
||||
{file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"},
|
||||
{file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
requires_python = ">=3.5"
|
||||
summary = "Type system extensions for programs checked with the mypy type checker."
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Core utilities for Python packages"
|
||||
files = [
|
||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Utility library for gitignore style pattern matching of file paths."
|
||||
files = [
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.2.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
files = [
|
||||
{file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
|
||||
{file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.4.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "plugin and hook calling mechanisms for python"
|
||||
files = [
|
||||
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
|
||||
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.9.1"
|
||||
requires_python = ">=3.6"
|
||||
summary = "Python style guide checker"
|
||||
files = [
|
||||
{file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
|
||||
{file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "2.5.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "passive checker of Python programs"
|
||||
files = [
|
||||
{file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
|
||||
{file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygls"
|
||||
version = "2.0.1"
|
||||
requires_python = ">=3.9"
|
||||
summary = "A pythonic generic language server (pronounced like 'pie glass')"
|
||||
dependencies = [
|
||||
"attrs>=24.3.0",
|
||||
"cattrs>=23.1.2",
|
||||
"lsprotocol==2025.0.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pygls-2.0.1-py3-none-any.whl", hash = "sha256:d29748042cea5bedc98285eb3e2c0c60bf3fc73786319519001bf72bbe8f36cc"},
|
||||
{file = "pygls-2.0.1.tar.gz", hash = "sha256:2f774a669fbe2ece977d302786f01f9b0c5df7d0204ea0fa371ecb08288d6b86"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.1.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "pytest: simple powerful testing with Python"
|
||||
dependencies = [
|
||||
"colorama; sys_platform == \"win32\"",
|
||||
"exceptiongroup>=1.0.0rc8; python_version < \"3.11\"",
|
||||
"iniconfig",
|
||||
"packaging",
|
||||
"pluggy<2.0,>=1.4",
|
||||
"tomli>=1; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"},
|
||||
{file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "5.0.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Pytest plugin for measuring coverage."
|
||||
dependencies = [
|
||||
"coverage[toml]>=5.2.1",
|
||||
"pytest>=4.6",
|
||||
]
|
||||
files = [
|
||||
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
|
||||
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-datadir"
|
||||
version = "1.5.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "pytest plugin for test data directories and files"
|
||||
dependencies = [
|
||||
"pytest>=5.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pytest-datadir-1.5.0.tar.gz", hash = "sha256:1617ed92f9afda0c877e4eac91904b5f779d24ba8f5e438752e3ae39d8d2ee3f"},
|
||||
{file = "pytest_datadir-1.5.0-py3-none-any.whl", hash = "sha256:34adf361bcc7b37961bbc1dfa8d25a4829e778bab461703c38a5c50ca9c36dc8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
summary = "Python 2 and 3 compatibility utilities"
|
||||
files = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
requires_python = ">=3.7"
|
||||
summary = "A lil' TOML parser"
|
||||
files = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.10.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
files = [
|
||||
{file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
|
||||
{file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
|
||||
]
|
||||
346
poetry.lock
generated
346
poetry.lock
generated
@@ -1,346 +0,0 @@
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Atomic file writes."
|
||||
name = "atomicwrites"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.3.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Classes Without Boilerplate"
|
||||
name = "attrs"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "19.3.0"
|
||||
|
||||
[[package]]
|
||||
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 = "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
|
||||
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]
|
||||
entrypoints = ">=0.3.0,<0.4.0"
|
||||
mccabe = ">=0.6.0,<0.7.0"
|
||||
pycodestyle = ">=2.5.0,<2.6.0"
|
||||
pyflakes = ">=2.1.0,<2.2.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Read metadata from Python packages"
|
||||
marker = "python_version < \"3.8\""
|
||||
name = "importlib-metadata"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3"
|
||||
version = "0.23"
|
||||
|
||||
[package.dependencies]
|
||||
zipp = ">=0.5"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
name = "isort"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "4.3.21"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "McCabe checker, plugin for flake8"
|
||||
name = "mccabe"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.6.1"
|
||||
|
||||
[[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"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "19.2"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2"
|
||||
six = "*"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
name = "pluggy"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "0.13.0"
|
||||
|
||||
[package.dependencies]
|
||||
[package.dependencies.importlib-metadata]
|
||||
python = "<3.8"
|
||||
version = ">=0.12"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
name = "py"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.8.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Python style guide checker"
|
||||
name = "pycodestyle"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
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]]
|
||||
category = "main"
|
||||
description = "a pythonic generic language server (pronounced like \"pie glass\")."
|
||||
name = "pygls"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.8.1"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Python parsing module"
|
||||
name = "pyparsing"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
version = "2.4.2"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
name = "pytest"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "5.2.1"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = ">=1.0"
|
||||
attrs = ">=17.4.0"
|
||||
colorama = "*"
|
||||
more-itertools = ">=4.0.0"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<1.0"
|
||||
py = ">=1.5.0"
|
||||
wcwidth = "*"
|
||||
|
||||
[package.dependencies.importlib-metadata]
|
||||
python = "<3.8"
|
||||
version = ">=0.12"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "pytest plugin for test data directories and files"
|
||||
name = "pytest-datadir"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.3.1"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=2.7.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
name = "six"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
|
||||
version = "1.12.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
name = "toml"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.10.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "tox is a generic virtualenv management and test command line tool"
|
||||
name = "tox"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "3.14.0"
|
||||
|
||||
[package.dependencies]
|
||||
filelock = ">=3.0.0,<4"
|
||||
packaging = ">=14"
|
||||
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]]
|
||||
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"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "3.7.4"
|
||||
|
||||
[package.dependencies]
|
||||
typing = ">=3.7.4"
|
||||
|
||||
[[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"
|
||||
optional = false
|
||||
python-versions = ">=2.7"
|
||||
version = "0.6.0"
|
||||
|
||||
[package.dependencies]
|
||||
more-itertools = "*"
|
||||
|
||||
[metadata]
|
||||
content-hash = "2fa2f64a1c51f6312594f611baa29f434f3a8ede16543988ac0d76a6de587c7f"
|
||||
python-versions = "^3.6"
|
||||
|
||||
[metadata.hashes]
|
||||
atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"]
|
||||
attrs = ["08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"]
|
||||
colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"]
|
||||
entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"]
|
||||
filelock = ["18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", "929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"]
|
||||
flake8 = ["19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", "8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"]
|
||||
importlib-metadata = ["aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"]
|
||||
isort = ["54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", "6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"]
|
||||
mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"]
|
||||
more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"]
|
||||
mypy = ["1521c186a3d200c399bd5573c828ea2db1362af7209b2adb1bb8532cea2fb36f", "31a046ab040a84a0fc38bc93694876398e62bc9f35eca8ccbf6418b7297f4c00", "3b1a411909c84b2ae9b8283b58b48541654b918e8513c20a400bb946aa9111ae", "48c8bc99380575deb39f5d3400ebb6a8a1cb5cc669bbba4d3bb30f904e0a0e7d", "540c9caa57a22d0d5d3c69047cc9dd0094d49782603eb03069821b41f9e970e9", "672e418425d957e276c291930a3921b4a6413204f53fe7c37cad7bc57b9a3391", "6ed3b9b3fdc7193ea7aca6f3c20549b377a56f28769783a8f27191903a54170f", "9371290aa2cad5ad133e4cdc43892778efd13293406f7340b9ffe99d5ec7c1d9", "ace6ac1d0f87d4072f05b5468a084a45b4eda970e4d26704f201e06d47ab2990", "b428f883d2b3fe1d052c630642cc6afddd07d5cd7873da948644508be3b9d4a7", "d5bf0e6ec8ba346a2cf35cb55bf4adfddbc6b6576fcc9e10863daa523e418dbb", "d7574e283f83c08501607586b3167728c58e8442947e027d2d4c7dcd6d82f453", "dc889c84241a857c263a2b1cd1121507db7d5b5f5e87e77147097230f374d10b", "f4748697b349f373002656bf32fede706a0e713d67bfdcf04edf39b1f61d46eb"]
|
||||
mypy-extensions = ["090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", "2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"]
|
||||
packaging = ["28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", "d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"]
|
||||
pluggy = ["0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", "fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"]
|
||||
py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"]
|
||||
pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"]
|
||||
pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"]
|
||||
pygls = ["3ee878a828b7bc0873a2ea44208d6846a91aa7dbbbdc052e7fe8cc689f6644fa", "780fd0c5ae95ad02ecaf70b071e43ff8ced8384b7d6bed19311a7b431d26fb88"]
|
||||
pyparsing = ["6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", "d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"]
|
||||
pytest = ["7e4800063ccfc306a53c461442526c5571e1462f61583506ce97e4da6a1d88c8", "ca563435f4941d0cb34767301c27bc65c510cb82e90b9ecf9cb52dc2c63caaa0"]
|
||||
pytest-datadir = ["1847ed0efe0bc54cac40ab3fba6d651c2f03d18dd01f2a582979604d32e7621e", "d3af1e738df87515ee509d6135780f25a15959766d9c2b2dbe02bf4fb979cb18"]
|
||||
six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"]
|
||||
toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"]
|
||||
tox = ["0bc216b6a2e6afe764476b4a07edf2c1dab99ed82bb146a1130b2e828f5bff5e", "c4f6b319c20ba4913dbfe71ebfd14ff95d1853c4231493608182f66e566ecfe1"]
|
||||
typed-ast = ["1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", "18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", "48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", "4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", "7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", "838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", "95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", "ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"]
|
||||
typing = ["91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23", "c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36", "f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714"]
|
||||
typing-extensions = ["2ed632b30bb54fc3941c382decfd0ee4148f5c591651c9272473fea2c6397d95", "b1edbbf0652660e32ae780ac9433f4231e7339c7f9a8057d0f042fcbcea49b87", "d8179012ec2c620d3791ca6fe2bf7979d979acdbef1fca0bc56b37411db682ed"]
|
||||
virtualenv = ["11cb4608930d5fd3afb545ecf8db83fa50e1f96fc4fca80c94b07d2c83146589", "d257bb3773e48cac60e475a19b608996c73f4d333b3ba2e4e57d5ac6134e0136"]
|
||||
wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"]
|
||||
yapf = ["02ace10a00fa2e36c7ebd1df2ead91dbfbd7989686dc4ccbdc549e95d19f5780", "6f94b6a176a7c114cfa6bad86d40f259bbe0f10cf2fa7f2f4b3596fc5802a41b"]
|
||||
zipp = ["3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", "f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"]
|
||||
@@ -1,40 +1,61 @@
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "cmake-language-server"
|
||||
version = "0.1.0"
|
||||
dynamic = ["version"]
|
||||
description = "CMake LSP Implementation"
|
||||
license = "MIT"
|
||||
authors = ["regen"]
|
||||
authors = [
|
||||
{name = "Regen"},
|
||||
]
|
||||
dependencies = [
|
||||
"pygls>=2.0.0",
|
||||
]
|
||||
requires-python = ">=3.10.0,<3.16"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/regen100/cmake-language-server"
|
||||
license = {text = "MIT"}
|
||||
keywords = ["cmake", "completion", "vim", "lsp"]
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Environment :: Console",
|
||||
"Development Status :: 3 - Alpha", "Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: OS Independent",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Text Editors :: Integrated Development Environments (IDE)",
|
||||
"Topic :: Utilities"
|
||||
"Topic :: Text Editors :: Integrated Development Environments (IDE)", "Topic :: Utilities",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.6"
|
||||
pygls = "^0.8.1"
|
||||
pyparsing = "^2.4"
|
||||
[project.urls]
|
||||
repository = "https://github.com/regen100/cmake-language-server"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
flake8 = "^3.7"
|
||||
mypy = "^0.740.0"
|
||||
pytest = "^5.2"
|
||||
yapf = "^0.28.0"
|
||||
pytest-datadir = "^1.3"
|
||||
tox = "^3.14"
|
||||
isort = "^4.3"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
cmake-format = "cmake_language_server.formatter:main"
|
||||
[project.scripts]
|
||||
cmake-language-server = "cmake_language_server.server:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry>=0.12"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
requires = ["pdm-backend"]
|
||||
build-backend = "pdm.backend"
|
||||
|
||||
[tool.pdm.version]
|
||||
source = "scm"
|
||||
write_to = "cmake_language_server/version.py"
|
||||
write_template = "__version__ = \"{}\"\n"
|
||||
|
||||
[tool.pdm.dev-dependencies]
|
||||
dev = [
|
||||
"pytest>=7.2.0",
|
||||
"pytest-datadir>=1.4.1",
|
||||
"pytest-cov>=4.0.0",
|
||||
"cmakelang>=0.6.13",
|
||||
]
|
||||
lint = [
|
||||
"mypy>=0.991",
|
||||
"flake8>=5.0.4",
|
||||
"black>=22.12.0",
|
||||
"isort>=5.11.4",
|
||||
]
|
||||
|
||||
[tool.pdm.scripts]
|
||||
test = "pytest --cov-report=term --cov-report=xml --cov=cmake_language_server -sv tests"
|
||||
|
||||
[tool.pdm.scripts.lint]
|
||||
shell = """
|
||||
isort --check cmake_language_server tests
|
||||
black --check cmake_language_server tests
|
||||
flake8 cmake_language_server tests
|
||||
mypy cmake_language_server tests
|
||||
"""
|
||||
|
||||
12
setup.cfg
Normal file
12
setup.cfg
Normal file
@@ -0,0 +1,12 @@
|
||||
[flake8]
|
||||
max-line-length = 88
|
||||
extend-ignore = E203
|
||||
|
||||
[isort]
|
||||
profile = black
|
||||
line_length = 88
|
||||
|
||||
[mypy]
|
||||
strict = true
|
||||
ignore_missing_imports = true
|
||||
show_error_codes = true
|
||||
@@ -1 +0,0 @@
|
||||
__version__ = '0.1.0'
|
||||
@@ -1,257 +0,0 @@
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Pattern
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class API(object):
|
||||
_cmake: str
|
||||
_build: Path
|
||||
_uuid: uuid.UUID
|
||||
_builtin_commands: Dict[str, str]
|
||||
_builtin_variables: Dict[str, str]
|
||||
_builtin_variable_template: Dict[Pattern, str]
|
||||
_targets: List[str]
|
||||
_cached_variables: Dict[str, str]
|
||||
_generated_list_parsed: bool
|
||||
|
||||
def __init__(self, cmake: str, build: Path):
|
||||
self._cmake = cmake
|
||||
self._build = Path(build)
|
||||
self._uuid = uuid.uuid4()
|
||||
|
||||
self._builtin_commands = {}
|
||||
self._builtin_variables = {}
|
||||
self._builtin_variable_template = {}
|
||||
self._targets = []
|
||||
self._cached_variables = {}
|
||||
self._generated_list_parsed = False
|
||||
|
||||
def query(self) -> bool:
|
||||
if not self.cmake_cache.exists():
|
||||
return False
|
||||
|
||||
self.query_json.parent.mkdir(parents=True, exist_ok=True)
|
||||
with self.query_json.open('w') as fp:
|
||||
fp.write('''\
|
||||
{
|
||||
"requests": [
|
||||
{"kind": "codemodel", "version": 2},
|
||||
{"kind": "cache", "version": 2},
|
||||
{"kind": "cmakeFiles", "version": 1}
|
||||
]
|
||||
}''')
|
||||
|
||||
proc = subprocess.run([self._cmake, self._build],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
self.query_json.unlink()
|
||||
self.query_json.parent.rmdir()
|
||||
if proc.returncode != 0:
|
||||
logging.error(
|
||||
f'cmake exited with {proc.returncode}: {proc.stderr}')
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def read_reply(self) -> bool:
|
||||
reply = self._build / '.cmake' / 'api' / 'v1' / 'reply'
|
||||
indices = sorted(reply.glob('index-*.json'))
|
||||
if not indices:
|
||||
logger.error('no reply')
|
||||
return False
|
||||
with indices[-1].open() as fp:
|
||||
index = json.load(fp)
|
||||
try:
|
||||
responses = index['reply'][f'client-{self._uuid}']['query.json'][
|
||||
'responses']
|
||||
except KeyError:
|
||||
logger.error('no rensponse')
|
||||
return False
|
||||
for response in responses:
|
||||
if response['kind'] == 'codemodel':
|
||||
self._read_codemodel(reply / response['jsonFile'])
|
||||
elif response['kind'] == 'cache':
|
||||
self._read_cache(reply / response['jsonFile'])
|
||||
elif response['kind'] == 'cmakeFiles':
|
||||
self._read_cmake_files(reply / response['jsonFile'])
|
||||
|
||||
return True
|
||||
|
||||
def _read_codemodel(self, codemodelpath: Path):
|
||||
with (codemodelpath).open() as fp:
|
||||
codemodel = json.load(fp)
|
||||
config = codemodel['configurations'][0]
|
||||
self._targets[:] = [x['name'] for x in config['targets']]
|
||||
|
||||
def _read_cache(self, cachepath: Path):
|
||||
with cachepath.open() as fp:
|
||||
cache = json.load(fp)
|
||||
self._cached_variables.clear()
|
||||
for entry in cache['entries']:
|
||||
name = entry['name']
|
||||
value = self._truncate_variable(entry['value'])
|
||||
properties = {x['name']: x['value'] for x in entry['properties']}
|
||||
helpstring = properties.get('HELPSTRING', '')
|
||||
doc = []
|
||||
if helpstring:
|
||||
doc.append(helpstring)
|
||||
if value:
|
||||
doc.append(f'`{value}`')
|
||||
self._cached_variables[name] = '\n\n'.join(doc)
|
||||
|
||||
def _read_cmake_files(self, jsonpath: Path):
|
||||
'''inspect generated list files'''
|
||||
|
||||
if not self._builtin_variables or self._generated_list_parsed:
|
||||
return
|
||||
|
||||
with jsonpath.open() as fp:
|
||||
cmake_files = json.load(fp)
|
||||
|
||||
# inspect generated list files
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
tmplist = Path(tmpdirname) / 'dump.cmake'
|
||||
with tmplist.open('w') as fp:
|
||||
for listfile in cmake_files['inputs']:
|
||||
if not listfile.get('isGenerated', False):
|
||||
continue
|
||||
path = listfile['path']
|
||||
fp.write(f'include({path})\n')
|
||||
fp.write('''
|
||||
get_cmake_property(variables VARIABLES)
|
||||
foreach (variable ${variables})
|
||||
message("${variable}=${${variable}}")
|
||||
endforeach()
|
||||
''')
|
||||
p = subprocess.run([self._cmake, '-P', tmplist],
|
||||
cwd=cmake_files['paths']['source'],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
if p.returncode != 0:
|
||||
return
|
||||
|
||||
for line in p.stderr.split('\n'):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
k, v = line.split('=', 1)
|
||||
if k.startswith('CMAKE_ARG'):
|
||||
continue
|
||||
v = self._truncate_variable(v)
|
||||
if k in self._builtin_variables:
|
||||
self._builtin_variables[k] += f'\n\n`{v}`'
|
||||
else:
|
||||
for pattern, doc in self._builtin_variable_template.items(
|
||||
):
|
||||
if pattern.fullmatch(k):
|
||||
self._builtin_variables[k] = f'{doc}\n\n`{v}`'
|
||||
break
|
||||
else:
|
||||
# ignore variable with no document
|
||||
pass
|
||||
|
||||
self._generated_list_parsed = True
|
||||
|
||||
@property
|
||||
def query_json(self) -> Path:
|
||||
return (self._build / '.cmake' / 'api' / 'v1' / 'query' /
|
||||
f'client-{self._uuid}' / 'query.json')
|
||||
|
||||
@property
|
||||
def cmake_cache(self) -> Path:
|
||||
return self._build / 'CMakeCache.txt'
|
||||
|
||||
def parse_doc(self) -> None:
|
||||
self._parse_commands()
|
||||
self._parse_variables()
|
||||
|
||||
def _parse_commands(self) -> None:
|
||||
p = subprocess.run([self._cmake, '--help-commands'],
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
|
||||
if p.returncode != 0:
|
||||
return
|
||||
|
||||
matches = re.finditer(
|
||||
r'''
|
||||
(?P<command>.+)\n
|
||||
-+\n\n
|
||||
[\s\S]*?
|
||||
(?P<signature>\ (?P=command)\s*\([^)]*\))
|
||||
''', p.stdout, re.VERBOSE)
|
||||
self._builtin_commands.clear()
|
||||
for match in matches:
|
||||
command = match.group('command')
|
||||
signature = match.group('signature')
|
||||
signature = re.sub(r'^ ', r'', signature, flags=re.MULTILINE)
|
||||
self._builtin_commands[
|
||||
command] = '```cmake\n' + signature + '\n```'
|
||||
|
||||
def _parse_variables(self) -> None:
|
||||
p = subprocess.run([self._cmake, '--help-variables'],
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
|
||||
if p.returncode != 0:
|
||||
return
|
||||
|
||||
matches = re.finditer(
|
||||
r'''
|
||||
(?P<variable>.+)\n
|
||||
-+\n\n
|
||||
(?P<doc>[\s\S]+?)(?:\n\n|$)
|
||||
''', p.stdout, re.VERBOSE)
|
||||
self._builtin_variables.clear()
|
||||
for match in matches:
|
||||
variable = match.group('variable')
|
||||
doc = match.group('doc')
|
||||
doc = re.sub(r':.+:`(.+)`', r'\1', doc)
|
||||
doc = re.sub(r'``(.+)``', r'`\1`', doc)
|
||||
doc = doc.replace('\n', ' ')
|
||||
doc = doc.replace('. ', '. ')
|
||||
if variable == 'CMAKE_MATCH_<n>':
|
||||
for i in range(10):
|
||||
self._builtin_variables[f'CMAKE_MATCH_{i}'] = doc
|
||||
elif '<' in variable:
|
||||
variable = re.sub(r'<[^>]+>', r'[^_]+', variable)
|
||||
pattern = re.compile(variable)
|
||||
self._builtin_variable_template[pattern] = doc
|
||||
else:
|
||||
self._builtin_variables[variable] = doc
|
||||
|
||||
def get_command_doc(self, command: str) -> Optional[str]:
|
||||
return self._builtin_commands.get(command)
|
||||
|
||||
def search_command(self, command: str) -> List[str]:
|
||||
command = command.lower()
|
||||
return [x for x in self._builtin_commands if x.startswith(command)]
|
||||
|
||||
def get_variable_doc(self, variable: str) -> Optional[str]:
|
||||
doc = self._cached_variables.get(variable)
|
||||
if doc:
|
||||
return doc
|
||||
return self._builtin_variables.get(variable)
|
||||
|
||||
def search_variable(self, variable: str) -> List[str]:
|
||||
cached = frozenset(x for x in self._cached_variables
|
||||
if x.startswith(variable))
|
||||
builtin = frozenset(x for x in self._builtin_variables
|
||||
if x.startswith(variable))
|
||||
return list(cached | builtin)
|
||||
|
||||
def search_target(self, target: str) -> List[str]:
|
||||
return [x for x in self._targets if x.startswith(target)]
|
||||
|
||||
def _truncate_variable(self, v: str) -> str:
|
||||
width = 70
|
||||
return v[:width] + (v[width:] and '...')
|
||||
@@ -1,123 +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 pathlib import Path
|
||||
from argparse import ArgumentParser
|
||||
from .parser import ListParser
|
||||
from difflib import unified_diff
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('lists', type=Path, nargs='*', help='CMake list files')
|
||||
parser.add_argument('-i',
|
||||
'--inplace',
|
||||
action='store_true',
|
||||
help='inplace edit')
|
||||
parser.add_argument('-d', '--diff', action='store_true', help='show diff')
|
||||
|
||||
args = parser.parse_args(args)
|
||||
|
||||
list_parser = ListParser()
|
||||
formatter = Formatter()
|
||||
for listpath in args.lists:
|
||||
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)
|
||||
else:
|
||||
if args.diff:
|
||||
diff = unified_diff(content.splitlines(True),
|
||||
formatted.splitlines(True), str(listpath),
|
||||
str(listpath), '(before formatting)',
|
||||
'(after formatting)')
|
||||
diffstr = ''.join(diff)
|
||||
if diffstr:
|
||||
print(diffstr, end='')
|
||||
else:
|
||||
print(formatted, end='')
|
||||
@@ -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
|
||||
@@ -1,138 +0,0 @@
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from pygls.features import (COMPLETION, FORMATTING, HOVER, INITIALIZE,
|
||||
INITIALIZED, TEXT_DOCUMENT_DID_SAVE)
|
||||
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 .formatter import Formatter
|
||||
from .parser import ListParser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CMakeLanguageServer(LanguageServer):
|
||||
_parser: ListParser
|
||||
_api: API
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self._parser = ListParser()
|
||||
self._api = None
|
||||
|
||||
@self.feature(INITIALIZE)
|
||||
def initialize(params: InitializeParams):
|
||||
opts = params.initializationOptions
|
||||
|
||||
cmake = getattr(opts, 'cmakeExecutable', 'cmake')
|
||||
builddir = getattr(opts, 'buildDirectory', '')
|
||||
logging.info(f'cmakeExecutable={cmake}, buildDirectory={builddir}')
|
||||
|
||||
self._api = API(cmake, Path(builddir))
|
||||
self._api.parse_doc()
|
||||
|
||||
@self.feature(COMPLETION, trigger_characters=['{'])
|
||||
def completions(params: CompletionParams):
|
||||
if (params.context.triggerKind ==
|
||||
CompletionTriggerKind.TriggerCharacter):
|
||||
token = ''
|
||||
trigger = params.context.triggerCharacter
|
||||
else:
|
||||
ret = self.cursor_word(params.textDocument.uri,
|
||||
params.position, False)
|
||||
if not ret:
|
||||
return None
|
||||
token = ret[0]
|
||||
trigger = None
|
||||
|
||||
items: List[CompletionItem] = []
|
||||
|
||||
if trigger != '{':
|
||||
commands = self._api.search_command(token)
|
||||
items.extend(
|
||||
CompletionItem(x,
|
||||
CompletionItemKind.Function,
|
||||
documentation=self._api.get_command_doc(x))
|
||||
for x in commands)
|
||||
|
||||
variables = self._api.search_variable(token)
|
||||
items.extend(
|
||||
CompletionItem(x,
|
||||
CompletionItemKind.Variable,
|
||||
documentation=self._api.get_variable_doc(x))
|
||||
for x in variables)
|
||||
|
||||
if trigger != '{':
|
||||
targets = self._api.search_target(token)
|
||||
items.extend(
|
||||
CompletionItem(x, CompletionItemKind.Class)
|
||||
for x in targets)
|
||||
|
||||
return CompletionList(False, items)
|
||||
|
||||
@self.feature(FORMATTING)
|
||||
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)
|
||||
lines = content.count('\n')
|
||||
return [
|
||||
TextEdit(Range(Position(0, 0), Position(lines + 1, 0)),
|
||||
formatted)
|
||||
]
|
||||
|
||||
@self.feature(HOVER)
|
||||
def hover(params: TextDocumentPositionParams):
|
||||
ret = self.cursor_word(params.textDocument.uri, params.position)
|
||||
if not ret:
|
||||
return None
|
||||
doc = self._api.get_command_doc(ret[0].lower())
|
||||
if not doc:
|
||||
doc = self._api.get_variable_doc(ret[0])
|
||||
if not doc:
|
||||
return None
|
||||
return Hover(MarkupContent(MarkupKind.Markdown, doc), ret[1])
|
||||
|
||||
@self.thread()
|
||||
@self.feature(TEXT_DOCUMENT_DID_SAVE, includeText=False)
|
||||
@self.feature(INITIALIZED)
|
||||
def run_cmake(*args):
|
||||
if self._api.query():
|
||||
self._api.read_reply()
|
||||
|
||||
def cursor_word(self,
|
||||
uri: str,
|
||||
position: Position,
|
||||
include_all: bool = True) -> Optional[Tuple[str, Range]]:
|
||||
doc = self.workspace.get_document(uri)
|
||||
content = doc.source
|
||||
line = content.split('\n')[position.line]
|
||||
cursor = position.character
|
||||
for m in re.finditer(r'\w+', line):
|
||||
if m.start() <= cursor <= m.end():
|
||||
end = m.end() if include_all else cursor
|
||||
return (line[m.start():end],
|
||||
Range(Position(position.line, m.start()),
|
||||
Position(position.line, end)))
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logging.getLogger('pygls').setLevel(logging.WARNING)
|
||||
CMakeLanguageServer().start_io()
|
||||
@@ -1,16 +1,60 @@
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE, run
|
||||
from threading import Thread
|
||||
from typing import Iterable, Tuple
|
||||
|
||||
import pytest
|
||||
from lsprotocol.types import EXIT, SHUTDOWN
|
||||
from pygls.lsp.server import LanguageServer
|
||||
|
||||
from cmake_language_server.server import CMakeLanguageServer
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def cmake_build(shared_datadir):
|
||||
from subprocess import run, PIPE
|
||||
source = shared_datadir / 'cmake'
|
||||
build = source / 'build'
|
||||
def cmake_build(shared_datadir: Path) -> Iterable[Path]:
|
||||
source = shared_datadir / "cmake"
|
||||
build = source / "build"
|
||||
build.mkdir()
|
||||
run(['cmake', source],
|
||||
check=True,
|
||||
p = run(
|
||||
["cmake", str(source)],
|
||||
cwd=build,
|
||||
stdout=PIPE,
|
||||
stderr=PIPE,
|
||||
universal_newlines=True)
|
||||
universal_newlines=True,
|
||||
)
|
||||
if p.returncode != 0:
|
||||
logging.error("stdout:\n" + p.stdout)
|
||||
logging.error("stderr:\n" + p.stderr)
|
||||
raise RuntimeError("CMake failed")
|
||||
yield build
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client_server() -> Iterable[Tuple[LanguageServer, CMakeLanguageServer]]:
|
||||
c2s_r, c2s_w = os.pipe()
|
||||
s2c_r, s2c_w = os.pipe()
|
||||
|
||||
def start(ls: LanguageServer, fdr: int, fdw: int) -> None:
|
||||
# start_io type hints seem to be wrong?
|
||||
ls.start_io(os.fdopen(fdr, "rb"), os.fdopen(fdw, "wb")) # type:ignore[arg-type]
|
||||
|
||||
server = CMakeLanguageServer("server", "v1")
|
||||
server_thread = Thread(target=start, args=(server, c2s_r, s2c_w))
|
||||
server_thread.start()
|
||||
|
||||
client = LanguageServer("client", "v1")
|
||||
client_thread = Thread(target=start, args=(client, s2c_r, c2s_w))
|
||||
client_thread.start()
|
||||
|
||||
yield client, server
|
||||
|
||||
# fix bug on python 3.7
|
||||
if hasattr(client.loop, "_signal_handlers"):
|
||||
client.loop._signal_handlers.clear()
|
||||
|
||||
client.protocol.send_request(SHUTDOWN) # type:ignore[no-untyped-call]
|
||||
client.protocol.notify(EXIT)
|
||||
client_thread.join()
|
||||
server_thread.join()
|
||||
|
||||
@@ -1,75 +1,122 @@
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from cmake_language_server.api import API
|
||||
|
||||
|
||||
def test_query_with_cache(cmake_build):
|
||||
api = API('cmake', cmake_build)
|
||||
def test_query_with_cache(cmake_build: Path) -> None:
|
||||
api = API("cmake", cmake_build)
|
||||
assert api.query()
|
||||
|
||||
query = cmake_build / '.cmake' / 'api' / 'v1' / 'query'
|
||||
query = cmake_build / ".cmake" / "api" / "v1" / "query"
|
||||
assert query.exists()
|
||||
|
||||
reply = cmake_build / '.cmake' / 'api' / 'v1' / 'reply'
|
||||
reply = cmake_build / ".cmake" / "api" / "v1" / "reply"
|
||||
assert reply.exists()
|
||||
|
||||
|
||||
def test_query_without_cache(cmake_build):
|
||||
api = API('cmake', cmake_build)
|
||||
(cmake_build / 'CMakeCache.txt').unlink()
|
||||
def test_query_without_cache(cmake_build: Path) -> None:
|
||||
api = API("cmake", cmake_build)
|
||||
(cmake_build / "CMakeCache.txt").unlink()
|
||||
|
||||
assert not api.query()
|
||||
|
||||
|
||||
def test_read_variable(cmake_build):
|
||||
api = API('cmake', cmake_build)
|
||||
def test_read_variable(cmake_build: Path) -> None:
|
||||
api = API("cmake", cmake_build)
|
||||
assert api.query()
|
||||
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):
|
||||
api = API('cmake', cmake_build)
|
||||
def test_read_cmake_files(cmake_build: Path) -> None:
|
||||
api = API("cmake", cmake_build)
|
||||
api.parse_doc()
|
||||
assert api.query()
|
||||
api.read_reply()
|
||||
|
||||
assert 'GNU' in api.get_variable_doc('CMAKE_CXX_COMPILER_ID')
|
||||
import platform
|
||||
|
||||
system = platform.system()
|
||||
cxx = api.get_variable_doc("CMAKE_CXX_COMPILER_ID")
|
||||
assert cxx is not None
|
||||
if system == "Linux":
|
||||
assert "GNU" in cxx
|
||||
elif system == "Windows":
|
||||
assert "MSVC" in cxx
|
||||
elif system == "Darwin":
|
||||
assert "Clang" in cxx
|
||||
else:
|
||||
raise RuntimeError("Unexpected system")
|
||||
|
||||
|
||||
def test_parse_commands(cmake_build):
|
||||
api = API('cmake', cmake_build)
|
||||
def test_parse_commands(cmake_build: Path) -> None:
|
||||
api = API("cmake", cmake_build)
|
||||
api.parse_doc()
|
||||
|
||||
p = subprocess.run(['cmake', '--help-command-list'],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
commands = p.stdout.strip().split('\n')
|
||||
p = subprocess.run(
|
||||
["cmake", "--help-command-list"],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
commands = p.stdout.strip().split("\n")
|
||||
|
||||
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')
|
||||
assert api.get_command_doc('not_existing_command') is None
|
||||
break_doc = api.get_command_doc("break")
|
||||
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):
|
||||
api = API('cmake', cmake_build)
|
||||
def test_parse_variables(cmake_build: Path) -> None:
|
||||
api = API("cmake", cmake_build)
|
||||
api.parse_doc()
|
||||
|
||||
p = subprocess.run(['cmake', '--help-variable-list'],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
variables = p.stdout.strip().split('\n')
|
||||
p = subprocess.run(
|
||||
["cmake", "--help-variable-list"],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
variables = p.stdout.strip().split("\n")
|
||||
|
||||
for variable in variables:
|
||||
if '<' in variable:
|
||||
if "<" in variable:
|
||||
continue
|
||||
assert api.get_variable_doc(
|
||||
variable) is not None, f'{variable} not found'
|
||||
assert api.get_variable_doc(variable) is not None, f"{variable} not found"
|
||||
|
||||
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("BUILD_SHARED_LIBS") is not None
|
||||
assert api.get_variable_doc("not_existing_variable") is None
|
||||
|
||||
|
||||
def test_parse_modules(cmake_build: Path) -> None:
|
||||
api = API("cmake", cmake_build)
|
||||
api.parse_doc()
|
||||
|
||||
p = subprocess.run(
|
||||
["cmake", "--help-module-list"],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
modules = p.stdout.strip().split("\n")
|
||||
|
||||
for module in modules:
|
||||
if module.startswith("Find"):
|
||||
assert (
|
||||
api.get_module_doc(module[4:], True) is not None
|
||||
), f"{module} not found"
|
||||
else:
|
||||
assert api.get_module_doc(module, False) is not None, f"{module} not found"
|
||||
|
||||
assert api.get_module_doc("GoogleTest", False) is not None
|
||||
assert api.get_module_doc("GoogleTest", True) is None
|
||||
assert api.search_module("GoogleTest", False) == ["GoogleTest"]
|
||||
assert api.search_module("GoogleTest", True) == []
|
||||
assert api.get_module_doc("Boost", False) is None
|
||||
assert api.get_module_doc("Boost", True) is not None
|
||||
assert api.search_module("Boost", False) == []
|
||||
assert api.search_module("Boost", True) == ["Boost"]
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
from cmake_language_server.formatter import Formatter
|
||||
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()
|
||||
''')
|
||||
@@ -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')
|
||||
196
tests/test_server.py
Normal file
196
tests/test_server.py
Normal file
@@ -0,0 +1,196 @@
|
||||
import time
|
||||
from concurrent import futures
|
||||
from pathlib import Path
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import pytest
|
||||
from lsprotocol.types import (
|
||||
INITIALIZE,
|
||||
TEXT_DOCUMENT_COMPLETION,
|
||||
TEXT_DOCUMENT_DID_OPEN,
|
||||
TEXT_DOCUMENT_FORMATTING,
|
||||
TEXT_DOCUMENT_HOVER,
|
||||
WORKSPACE_DID_CHANGE_CONFIGURATION,
|
||||
ClientCapabilities,
|
||||
CompletionContext,
|
||||
CompletionList,
|
||||
CompletionParams,
|
||||
CompletionTriggerKind,
|
||||
DidChangeConfigurationParams,
|
||||
DidOpenTextDocumentParams,
|
||||
DocumentFormattingParams,
|
||||
FormattingOptions,
|
||||
HoverParams,
|
||||
InitializeParams,
|
||||
MarkupContent,
|
||||
Position,
|
||||
TextDocumentIdentifier,
|
||||
TextDocumentItem,
|
||||
)
|
||||
from pygls.lsp.server import LanguageServer
|
||||
|
||||
from cmake_language_server.server import CMakeLanguageServer
|
||||
|
||||
CALL_TIMEOUT = 2
|
||||
|
||||
|
||||
def _init(client: LanguageServer, root: Path) -> None:
|
||||
retry = 3
|
||||
while retry > 0:
|
||||
try:
|
||||
client.protocol.send_request( # type:ignore[no-untyped-call]
|
||||
INITIALIZE,
|
||||
InitializeParams(
|
||||
process_id=1234,
|
||||
root_uri=root.as_uri(),
|
||||
capabilities=ClientCapabilities(),
|
||||
),
|
||||
).result(timeout=CALL_TIMEOUT)
|
||||
except futures.TimeoutError:
|
||||
retry -= 1
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def _open(client: LanguageServer, path: Path, text: Optional[str] = None) -> None:
|
||||
if text is None:
|
||||
text = path.read_text()
|
||||
|
||||
client.protocol.notify(
|
||||
TEXT_DOCUMENT_DID_OPEN,
|
||||
DidOpenTextDocumentParams(
|
||||
text_document=TextDocumentItem(
|
||||
uri=path.as_uri(), language_id="cmake", version=1, text=text
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _test_completion(
|
||||
client_server: Tuple[LanguageServer, CMakeLanguageServer],
|
||||
datadir: Path,
|
||||
content: str,
|
||||
context: Optional[CompletionContext],
|
||||
) -> CompletionList:
|
||||
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,
|
||||
)
|
||||
ret = client.protocol.send_request( # type:ignore[no-untyped-call]
|
||||
TEXT_DOCUMENT_COMPLETION, params
|
||||
).result(timeout=CALL_TIMEOUT)
|
||||
assert isinstance(ret, CompletionList)
|
||||
return ret
|
||||
|
||||
|
||||
def test_initialize(
|
||||
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||
) -> None:
|
||||
client, server = client_server
|
||||
|
||||
assert server._api is None
|
||||
_init(client, datadir)
|
||||
assert server._api is not None
|
||||
|
||||
|
||||
def test_workspace_did_change_configuration(
|
||||
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||
) -> None:
|
||||
client, server = client_server
|
||||
|
||||
_init(client, datadir)
|
||||
|
||||
old_api = server._api
|
||||
|
||||
client.protocol.notify(
|
||||
WORKSPACE_DID_CHANGE_CONFIGURATION,
|
||||
DidChangeConfigurationParams(
|
||||
settings={"initialization_options": {"buildDirectory": "c_build"}}
|
||||
),
|
||||
)
|
||||
|
||||
start = time.monotonic()
|
||||
while server._api is old_api and (time.monotonic() - start) < CALL_TIMEOUT:
|
||||
time.sleep(0.1)
|
||||
|
||||
assert server._api is not None
|
||||
assert server._api._build.as_posix() == "c_build"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"context", [CompletionContext(trigger_kind=CompletionTriggerKind.Invoked), None]
|
||||
)
|
||||
def test_completions(
|
||||
context: Optional[CompletionContext],
|
||||
client_server: Tuple[LanguageServer, CMakeLanguageServer],
|
||||
datadir: Path,
|
||||
) -> None:
|
||||
response = _test_completion(client_server, datadir, "projec", context)
|
||||
item = next(filter(lambda x: x.label == "project", response.items), None)
|
||||
assert item is not None
|
||||
assert isinstance(item.documentation, MarkupContent)
|
||||
assert "<PROJECT-NAME>" in item.documentation.value
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text, item",
|
||||
[("find_package(", "Boost"), ("include(", "GoogleTest"), ("${", "PROJECT_VERSION")],
|
||||
)
|
||||
def test_completions_triggercharacter(
|
||||
text: str,
|
||||
item: str,
|
||||
client_server: Tuple[LanguageServer, CMakeLanguageServer],
|
||||
datadir: Path,
|
||||
) -> None:
|
||||
response = _test_completion(
|
||||
client_server,
|
||||
datadir,
|
||||
text,
|
||||
CompletionContext(
|
||||
trigger_kind=CompletionTriggerKind.TriggerCharacter,
|
||||
trigger_character=text[-1],
|
||||
),
|
||||
)
|
||||
assert item in [x.label for x in response.items]
|
||||
|
||||
response_nocontext = _test_completion(client_server, datadir, text, None)
|
||||
assert response == response_nocontext
|
||||
|
||||
|
||||
def test_formatting(
|
||||
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||
) -> None:
|
||||
client, server = client_server
|
||||
_init(client, datadir)
|
||||
path = datadir / "CMakeLists.txt"
|
||||
_open(client, path, "a ( b c ) ")
|
||||
response = client.protocol.send_request( # type:ignore[no-untyped-call]
|
||||
TEXT_DOCUMENT_FORMATTING,
|
||||
DocumentFormattingParams(
|
||||
text_document=TextDocumentIdentifier(uri=path.as_uri()),
|
||||
options=FormattingOptions(tab_size=2, insert_spaces=True),
|
||||
),
|
||||
).result(timeout=CALL_TIMEOUT)
|
||||
assert response[0].new_text == "a(b c)\n"
|
||||
|
||||
|
||||
def test_hover(
|
||||
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||
) -> None:
|
||||
client, server = client_server
|
||||
_init(client, datadir)
|
||||
path = datadir / "CMakeLists.txt"
|
||||
_open(client, path, "project()")
|
||||
response = client.protocol.send_request( # type:ignore[no-untyped-call]
|
||||
TEXT_DOCUMENT_HOVER,
|
||||
HoverParams(
|
||||
text_document=TextDocumentIdentifier(uri=path.as_uri()),
|
||||
position=Position(line=0, character=0),
|
||||
),
|
||||
).result(timeout=CALL_TIMEOUT)
|
||||
assert "<PROJECT-NAME>" in response.contents.value
|
||||
34
tox.ini
34
tox.ini
@@ -1,27 +1,23 @@
|
||||
[tox]
|
||||
env_list = py{39,310,311,312,313,314}
|
||||
isolated_build = True
|
||||
skipsdist = True
|
||||
envlist = py36, py37, py38, lint
|
||||
passenv = *
|
||||
setenv =
|
||||
PDM_IGNORE_SAVED_PYTHON="1"
|
||||
|
||||
[gh-actions]
|
||||
python =
|
||||
3.6: py36
|
||||
3.7: py37, lint
|
||||
3.8: py38
|
||||
3.9: py39
|
||||
3.10: py310
|
||||
3.11: py311
|
||||
3.12: py312
|
||||
3.13: py313
|
||||
3.14: py314
|
||||
|
||||
[testenv]
|
||||
whitelist_externals = poetry
|
||||
skip_install = true
|
||||
commands_pre =
|
||||
poetry install
|
||||
allowlist_externals =
|
||||
pdm
|
||||
commands =
|
||||
poetry run pytest -sv tests
|
||||
|
||||
[testenv:lint]
|
||||
whitelist_externals = poetry
|
||||
skip_install = true
|
||||
commands =
|
||||
poetry run isort -c -rc src tests
|
||||
poetry run yapf -d -r src tests
|
||||
poetry run flake8
|
||||
poetry run mypy src tests
|
||||
pdm install --dev -G :all
|
||||
pdm run lint
|
||||
pdm run test
|
||||
|
||||
Reference in New Issue
Block a user