Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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
|
||||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* @regen100
|
||||
38
.github/workflows/publish.yml
vendored
38
.github/workflows/publish.yml
vendored
@@ -5,22 +5,26 @@ on:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-n-publish:
|
||||
name: Build and publish
|
||||
runs-on: ubuntu-18.04
|
||||
publish:
|
||||
name: Publish to PyPI
|
||||
runs-on: ubuntu-22.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 }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up PDM
|
||||
uses: pdm-project/setup-pdm@v3
|
||||
- name: Build
|
||||
run: |
|
||||
pdm build
|
||||
- name: Publish package
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.PYPI_PASSWORD }}
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
generate_release_notes: true
|
||||
|
||||
37
.github/workflows/tests.yml
vendored
37
.github/workflows/tests.yml
vendored
@@ -1,41 +1,54 @@
|
||||
name: Tests
|
||||
|
||||
on: [push]
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python: [3.6, 3.7, 3.8]
|
||||
os: [ubuntu-18.04, windows-2016]
|
||||
python: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||
os: [ubuntu-22.04, windows-2022]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v1
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- name: Set up PDM
|
||||
uses: pdm-project/setup-pdm@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
cache: true
|
||||
- name: Setup VC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
if: contains(matrix.os, 'windows')
|
||||
- name: Install CMake
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
CMAKE_VERSION=3.14.7
|
||||
curl -sSL https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz | tar xz
|
||||
sudo cp -rT cmake-$CMAKE_VERSION-Linux-x86_64 /usr/local
|
||||
rm -rf cmake-$CMAKE_VERSION-Linux-x86_64
|
||||
CMAKE_VERSION=3.25.1
|
||||
curl -sSL https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.tar.gz | tar xz
|
||||
sudo cp -rT cmake-${CMAKE_VERSION}-linux-x86_64 /usr/local
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cmake --version
|
||||
python -m pip install --upgrade setuptools pip wheel
|
||||
python -m pip install poetry tox-gh-actions
|
||||
python -m pip install tox tox-gh-actions
|
||||
- name: Test with tox
|
||||
run: |
|
||||
tox
|
||||
env:
|
||||
os: ${{ matrix.os }}
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: unittests
|
||||
name: Python ${{ matrix.python }} on ${{ matrix.os }}
|
||||
fail_ci_if_error: true
|
||||
fail_ci_if_error: false
|
||||
|
||||
130
.gitignore
vendored
130
.gitignore
vendored
@@ -1,133 +1,13 @@
|
||||
### https://raw.github.com/github/gitignore/cb0c6ef7ac68f2300409ee85501d9ad432cb4c7e/Python.gitignore
|
||||
cmake_language_server/version.py
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
/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/
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[style]
|
||||
based_on_style = pep8
|
||||
33
README.md
33
README.md
@@ -12,12 +12,11 @@ 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
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -25,12 +24,14 @@ Alpha Stage, work in progress.
|
||||
$ pip install cmake-language-server
|
||||
```
|
||||
|
||||
### Clients
|
||||
### Tested Clients
|
||||
|
||||
- Neovim ([neoclide/coc.nvim][coc.nvim])
|
||||
- Neovim ([neoclide/coc.nvim][coc.nvim], [prabirshrestha/vim-lsp][vim-lsp])
|
||||
|
||||
#### Neovim
|
||||
|
||||
##### coc.nvim
|
||||
|
||||
```jsonc
|
||||
"languageserver": {
|
||||
"cmake": {
|
||||
@@ -46,5 +47,29 @@ $ pip install cmake-language-server
|
||||
}
|
||||
```
|
||||
|
||||
##### vim-lsp
|
||||
|
||||
```vim
|
||||
if executable('cmake-language-server')
|
||||
au User lsp_setup call lsp#register_server({
|
||||
\ 'name': 'cmake',
|
||||
\ 'cmd': {server_info->['cmake-language-server']},
|
||||
\ 'root_uri': {server_info->lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'build/'))},
|
||||
\ 'whitelist': ['cmake'],
|
||||
\ 'initialization_options': {
|
||||
\ 'buildDirectory': 'build',
|
||||
\ }
|
||||
\})
|
||||
endif
|
||||
```
|
||||
|
||||
### Configuration
|
||||
* `buildDirectory`
|
||||
This language server uses CMake's file API to get cached variables.
|
||||
The API communicates using `<buildDirectory>/.cmake/api/`.
|
||||
`buildDirectory` is relative path to the root uri of the workspace.
|
||||
To configure the build tree, you need to run the cmake command such as `cmake .. -DFOO=bar`.
|
||||
|
||||
|
||||
[coc.nvim]: https://github.com/neoclide/coc.nvim
|
||||
[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 "...")
|
||||
267
cmake_language_server/server.py
Normal file
267
cmake_language_server/server.py
Normal file
@@ -0,0 +1,267 @@
|
||||
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,
|
||||
CompletionItem,
|
||||
CompletionItemKind,
|
||||
CompletionList,
|
||||
CompletionOptions,
|
||||
CompletionParams,
|
||||
CompletionTriggerKind,
|
||||
DocumentFormattingParams,
|
||||
Hover,
|
||||
InitializeParams,
|
||||
MarkupContent,
|
||||
MarkupKind,
|
||||
Position,
|
||||
Range,
|
||||
SaveOptions,
|
||||
TextDocumentPositionParams,
|
||||
TextEdit,
|
||||
)
|
||||
from pygls.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
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
@@ -4,5 +4,4 @@ coverage:
|
||||
default:
|
||||
threshold: 10%
|
||||
patch:
|
||||
default:
|
||||
threshold: 20%
|
||||
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
|
||||
568
pdm.lock
generated
Normal file
568
pdm.lock
generated
Normal file
@@ -0,0 +1,568 @@
|
||||
# This file is @generated by PDM.
|
||||
# It is not intended for manual editing.
|
||||
|
||||
[metadata]
|
||||
groups = ["default", "dev", "lint"]
|
||||
cross_platform = true
|
||||
static_urls = false
|
||||
lock_version = "4.3"
|
||||
content_hash = "sha256:80a5c705d477865a92cd50fa41774545541967eaa96e64bf34514c5baf4e3d6d"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "22.2.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "Classes Without Boilerplate"
|
||||
files = [
|
||||
{file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"},
|
||||
{file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "22.12.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "The uncompromising code formatter."
|
||||
dependencies = [
|
||||
"click>=8.0.0",
|
||||
"mypy-extensions>=0.4.3",
|
||||
"pathspec>=0.9.0",
|
||||
"platformdirs>=2",
|
||||
"tomli>=1.1.0; python_full_version < \"3.11.0a7\"",
|
||||
"typed-ast>=1.4.2; python_version < \"3.8\" and implementation_name == \"cpython\"",
|
||||
"typing-extensions>=3.10.0.0; python_version < \"3.10\"",
|
||||
]
|
||||
files = [
|
||||
{file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"},
|
||||
{file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"},
|
||||
{file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"},
|
||||
{file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"},
|
||||
{file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"},
|
||||
{file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"},
|
||||
{file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"},
|
||||
{file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"},
|
||||
{file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"},
|
||||
{file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"},
|
||||
{file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"},
|
||||
{file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cattrs"
|
||||
version = "22.2.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Composable complex class support for attrs and dataclasses."
|
||||
dependencies = [
|
||||
"attrs>=20",
|
||||
"exceptiongroup; python_version < \"3.11\"",
|
||||
"typing-extensions; python_version < \"3.8\"",
|
||||
]
|
||||
files = [
|
||||
{file = "cattrs-22.2.0-py3-none-any.whl", hash = "sha256:bc12b1f0d000b9f9bee83335887d532a1d3e99a833d1bf0882151c97d3e68c21"},
|
||||
{file = "cattrs-22.2.0.tar.gz", hash = "sha256:f0eed5642399423cf656e7b66ce92cdc5b963ecafd041d1b24d136fdde7acf6d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.3"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Composable command line interface toolkit"
|
||||
dependencies = [
|
||||
"colorama; platform_system == \"Windows\"",
|
||||
"importlib-metadata; python_version < \"3.8\"",
|
||||
]
|
||||
files = [
|
||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
||||
]
|
||||
|
||||
[[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.0.4"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Code coverage measurement for Python"
|
||||
files = [
|
||||
{file = "coverage-7.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:daf91db39324e9939a9db919ee4fb42a1a23634a056616dae891a030e89f87ba"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55121fe140d7e42cb970999b93cf1c2b24484ce028b32bbd00238bb25c13e34a"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c027fbb83a8c78a6e06a0302ea1799fdb70e5cda9845a5e000545b8e2b47ea39"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf82db5b7f16b51ec32fe0bd2da0805b177c807aa8bfb478c7e6f893418c284"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ba5cc54baf3c322c4388de2a43cc95f7809366f0600e743e5aae8ea9d1038b2"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:260854160083f8275a9d9d49a05ab0ffc7a1f08f2ccccbfaec94a18aae9f407c"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ea45f0dba5a993e93b158f1a9dcfff2770e3bcabf2b80dbe7aa15dce0bcb3bf3"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6abc91f6f8b3cc0ae1034e2c03f38769fba1952ab70d0b26953aa01691265c39"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-win32.whl", hash = "sha256:053cdc47cae08257051d7e934a0de4d095b60eb8a3024fa9f1b2322fa1547137"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:1e9e94f2612ee549a4b3ee79cbc61bceed77e69cf38cfa05858bae939a886d16"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5caa9dd91dcc5f054350dc57a02e053d79633907b9ccffff999568d13dcd19f8"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:efc200fa75d9634525b40babc7a16342bd21c101db1a58ef84dc14f4bf6ac0fd"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1791e5f74c5b52f76e83fe9f4bb9571cf76d40ee0c51952ee1e4ee935b7e98b9"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d9201cfa5a98652b9cef36ab202f17fe3ea83f497b4ba2a8ed39399dfb8fcd4"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22d8ef6865cb6834cab2b72fff20747a55c714b57b675f7e11c9624fe4f7cb45"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b84076e3de192fba0f95e279ac017b64c7c6ecd4f09f36f13420f5bed898a9c7"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:dcfbf8ffc046f20d75fd775a92c378f6fc7b9bded6c6f2ab88b6b9cb5805a184"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4665a714af31f160403c2e448fb2fef330719d2e04e836b08d60d612707c1041"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-win32.whl", hash = "sha256:2e59aef3fba5758059208c9eff10ae7ded3629e797972746ec33b56844f69411"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:2b854f7985b48122b6fe346631e86d67b63293f8255cb59a93d79e3d9f1574e3"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e44b60b0b49aa85d548d392a2dca2c6a581cd4084e72e9e16bd58bd86ec20816"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2904d7a0388911c61e7e3beefe48c29dfccaba938fc1158f63190101a21e04c2"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc74b64bfa89e2f862ea45dd6ac1def371d7cc883b76680d20bdd61a6f3daa20"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06046f54e719da21c79f98ecc0962581d1aee0b3798dc6b12b1217da8bf93f4"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bc9c77004970a364a1e5454cf7cb884e4277592b959c287689b2a0fd027ef552"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0815a09b32384e8ff00a5939ec9cd10efce8742347e019c2daca1a32f5ac2aae"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a78a80d131c067d67d8a6f9bd3d3f7ea7eac82c1c7259f97d7ab73f723da9d55"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-win32.whl", hash = "sha256:2b5936b624fbe711ed02dfd86edd678822e5ee68da02b6d231e5c01090b64590"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a63922765ee49d5b4c32afb2cd5516812c8665f3b78e64a0dd005bdfabf991b1"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d68f2f7bddb3acdd3b36ef7f334b9d14f30b93e094f808fbbd8d288b8f9e2f9b"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dafdba3b2b9010abab08cb8c0dc6549bfca6e1630fe14d47b01dca00d39e694"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0322354757b47640535daabd2d56384ff3cad2896248fc84d328c5fad4922d5c"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e8267466662aff93d66fa72b9591d02122dfc8a729b0a43dd70e0fb07ed9b37"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f684d88eb4924ed0630cf488fd5606e334c6835594bb5fe36b50a509b10383ed"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:70c294bb15ba576fb96b580db35895bf03749d683df044212b74e938a7f6821f"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:34c0457e1ba450ae8b22dc8ea2fd36ada1010af61291e4c96963cd9d9633366f"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b75aff2c35ceaa299691e772f7bf7c8aeab25f46acea2be3dd04cccb914a9860"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-win32.whl", hash = "sha256:6c5554d55668381e131577f20e8f620d4882b04ad558f7e7f3f1f55b3124c379"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c82f34fafaf5bc05d222fcf84423d6e156432ca35ca78672d4affd0c09c6ef6c"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8dfb5fed540f77e814bf4ec79619c241af6b4578fa1093c5e3389bbb7beab3f"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee32a080bab779b71c4d09a3eb5254bfca43ee88828a683dab27dfe8f582516e"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dfbee0bf0d633be3a2ab068f5a5731a70adf147d0ba17d9f9932b46c7c5782b"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32dc010713455ac0fe2fddb0e48aa43875cc7eb7b09768df10bad8ce45f9c430"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cb88a3019ad042eaa69fc7639ef077793fedbf313e89207aa82fefe92c97ebd"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:73bc6114aab7753ca784f87bcd3b7613bc797aa255b5bca45e5654070ae9acfb"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92f135d370fcd7a6fb9659fa2eb716dd2ca364719cbb1756f74d90a221bca1a7"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f3d485e6ec6e09857bf2115ece572d666b7c498377d4c70e66bb06c63ed177c2"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-win32.whl", hash = "sha256:c58921fcd9914b56444292e7546fe183d079db99528142c809549ddeaeacd8e9"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:f092d9f2ddaa30235d33335fbdb61eb8f3657af519ef5f9dd6bdae65272def11"},
|
||||
{file = "coverage-7.0.4-pp37.pp38.pp39-none-any.whl", hash = "sha256:cb8cfa3bf3a9f18211279458917fef5edeb5e1fdebe2ea8b11969ec2ebe48884"},
|
||||
{file = "coverage-7.0.4.tar.gz", hash = "sha256:f6c4ad409a0caf7e2e12e203348b1a9b19c514e7d078520973147bf2d3dcbc6f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.0.4"
|
||||
extras = ["toml"]
|
||||
requires_python = ">=3.7"
|
||||
summary = "Code coverage measurement for Python"
|
||||
dependencies = [
|
||||
"coverage==7.0.4",
|
||||
"tomli; python_full_version <= \"3.11.0a6\"",
|
||||
]
|
||||
files = [
|
||||
{file = "coverage-7.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:daf91db39324e9939a9db919ee4fb42a1a23634a056616dae891a030e89f87ba"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55121fe140d7e42cb970999b93cf1c2b24484ce028b32bbd00238bb25c13e34a"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c027fbb83a8c78a6e06a0302ea1799fdb70e5cda9845a5e000545b8e2b47ea39"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf82db5b7f16b51ec32fe0bd2da0805b177c807aa8bfb478c7e6f893418c284"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ba5cc54baf3c322c4388de2a43cc95f7809366f0600e743e5aae8ea9d1038b2"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:260854160083f8275a9d9d49a05ab0ffc7a1f08f2ccccbfaec94a18aae9f407c"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ea45f0dba5a993e93b158f1a9dcfff2770e3bcabf2b80dbe7aa15dce0bcb3bf3"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6abc91f6f8b3cc0ae1034e2c03f38769fba1952ab70d0b26953aa01691265c39"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-win32.whl", hash = "sha256:053cdc47cae08257051d7e934a0de4d095b60eb8a3024fa9f1b2322fa1547137"},
|
||||
{file = "coverage-7.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:1e9e94f2612ee549a4b3ee79cbc61bceed77e69cf38cfa05858bae939a886d16"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5caa9dd91dcc5f054350dc57a02e053d79633907b9ccffff999568d13dcd19f8"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:efc200fa75d9634525b40babc7a16342bd21c101db1a58ef84dc14f4bf6ac0fd"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1791e5f74c5b52f76e83fe9f4bb9571cf76d40ee0c51952ee1e4ee935b7e98b9"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d9201cfa5a98652b9cef36ab202f17fe3ea83f497b4ba2a8ed39399dfb8fcd4"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22d8ef6865cb6834cab2b72fff20747a55c714b57b675f7e11c9624fe4f7cb45"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b84076e3de192fba0f95e279ac017b64c7c6ecd4f09f36f13420f5bed898a9c7"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:dcfbf8ffc046f20d75fd775a92c378f6fc7b9bded6c6f2ab88b6b9cb5805a184"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4665a714af31f160403c2e448fb2fef330719d2e04e836b08d60d612707c1041"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-win32.whl", hash = "sha256:2e59aef3fba5758059208c9eff10ae7ded3629e797972746ec33b56844f69411"},
|
||||
{file = "coverage-7.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:2b854f7985b48122b6fe346631e86d67b63293f8255cb59a93d79e3d9f1574e3"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e44b60b0b49aa85d548d392a2dca2c6a581cd4084e72e9e16bd58bd86ec20816"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2904d7a0388911c61e7e3beefe48c29dfccaba938fc1158f63190101a21e04c2"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc74b64bfa89e2f862ea45dd6ac1def371d7cc883b76680d20bdd61a6f3daa20"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06046f54e719da21c79f98ecc0962581d1aee0b3798dc6b12b1217da8bf93f4"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bc9c77004970a364a1e5454cf7cb884e4277592b959c287689b2a0fd027ef552"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0815a09b32384e8ff00a5939ec9cd10efce8742347e019c2daca1a32f5ac2aae"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a78a80d131c067d67d8a6f9bd3d3f7ea7eac82c1c7259f97d7ab73f723da9d55"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-win32.whl", hash = "sha256:2b5936b624fbe711ed02dfd86edd678822e5ee68da02b6d231e5c01090b64590"},
|
||||
{file = "coverage-7.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a63922765ee49d5b4c32afb2cd5516812c8665f3b78e64a0dd005bdfabf991b1"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d68f2f7bddb3acdd3b36ef7f334b9d14f30b93e094f808fbbd8d288b8f9e2f9b"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dafdba3b2b9010abab08cb8c0dc6549bfca6e1630fe14d47b01dca00d39e694"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0322354757b47640535daabd2d56384ff3cad2896248fc84d328c5fad4922d5c"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e8267466662aff93d66fa72b9591d02122dfc8a729b0a43dd70e0fb07ed9b37"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f684d88eb4924ed0630cf488fd5606e334c6835594bb5fe36b50a509b10383ed"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:70c294bb15ba576fb96b580db35895bf03749d683df044212b74e938a7f6821f"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:34c0457e1ba450ae8b22dc8ea2fd36ada1010af61291e4c96963cd9d9633366f"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b75aff2c35ceaa299691e772f7bf7c8aeab25f46acea2be3dd04cccb914a9860"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-win32.whl", hash = "sha256:6c5554d55668381e131577f20e8f620d4882b04ad558f7e7f3f1f55b3124c379"},
|
||||
{file = "coverage-7.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c82f34fafaf5bc05d222fcf84423d6e156432ca35ca78672d4affd0c09c6ef6c"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8dfb5fed540f77e814bf4ec79619c241af6b4578fa1093c5e3389bbb7beab3f"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee32a080bab779b71c4d09a3eb5254bfca43ee88828a683dab27dfe8f582516e"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dfbee0bf0d633be3a2ab068f5a5731a70adf147d0ba17d9f9932b46c7c5782b"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32dc010713455ac0fe2fddb0e48aa43875cc7eb7b09768df10bad8ce45f9c430"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cb88a3019ad042eaa69fc7639ef077793fedbf313e89207aa82fefe92c97ebd"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:73bc6114aab7753ca784f87bcd3b7613bc797aa255b5bca45e5654070ae9acfb"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92f135d370fcd7a6fb9659fa2eb716dd2ca364719cbb1756f74d90a221bca1a7"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f3d485e6ec6e09857bf2115ece572d666b7c498377d4c70e66bb06c63ed177c2"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-win32.whl", hash = "sha256:c58921fcd9914b56444292e7546fe183d079db99528142c809549ddeaeacd8e9"},
|
||||
{file = "coverage-7.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:f092d9f2ddaa30235d33335fbdb61eb8f3657af519ef5f9dd6bdae65272def11"},
|
||||
{file = "coverage-7.0.4-pp37.pp38.pp39-none-any.whl", hash = "sha256:cb8cfa3bf3a9f18211279458917fef5edeb5e1fdebe2ea8b11969ec2ebe48884"},
|
||||
{file = "coverage-7.0.4.tar.gz", hash = "sha256:f6c4ad409a0caf7e2e12e203348b1a9b19c514e7d078520973147bf2d3dcbc6f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.1.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Backport of PEP 654 (exception groups)"
|
||||
files = [
|
||||
{file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"},
|
||||
{file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "5.0.4"
|
||||
requires_python = ">=3.6.1"
|
||||
summary = "the modular source code checker: pep8 pyflakes and co"
|
||||
dependencies = [
|
||||
"importlib-metadata<4.3,>=1.1.0; python_version < \"3.8\"",
|
||||
"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 = "importlib-metadata"
|
||||
version = "4.2.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "Read metadata from Python packages"
|
||||
dependencies = [
|
||||
"typing-extensions>=3.6.4; python_version < \"3.8\"",
|
||||
"zipp>=0.5",
|
||||
]
|
||||
files = [
|
||||
{file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"},
|
||||
{file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"},
|
||||
]
|
||||
|
||||
[[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.11.4"
|
||||
requires_python = ">=3.7.0"
|
||||
summary = "A Python utility / library to sort Python imports."
|
||||
files = [
|
||||
{file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"},
|
||||
{file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lsprotocol"
|
||||
version = "2023.0.0b1"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Python implementation of the Language Server Protocol."
|
||||
dependencies = [
|
||||
"attrs>=21.3.0",
|
||||
"cattrs",
|
||||
]
|
||||
files = [
|
||||
{file = "lsprotocol-2023.0.0b1-py3-none-any.whl", hash = "sha256:ade2cd0fa0ede7965698cb59cd05d3adbd19178fd73e83f72ef57a032fbb9d62"},
|
||||
{file = "lsprotocol-2023.0.0b1.tar.gz", hash = "sha256:f7a2d4655cbd5639f373ddd1789807450c543341fa0a32b064ad30dbb9f510d4"},
|
||||
]
|
||||
|
||||
[[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 = "0.991"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Optional static typing for Python"
|
||||
dependencies = [
|
||||
"mypy-extensions>=0.4.3",
|
||||
"tomli>=1.1.0; python_version < \"3.11\"",
|
||||
"typed-ast<2,>=1.4.0; python_version < \"3.8\"",
|
||||
"typing-extensions>=3.10",
|
||||
]
|
||||
files = [
|
||||
{file = "mypy-0.991-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab"},
|
||||
{file = "mypy-0.991-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d"},
|
||||
{file = "mypy-0.991-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6"},
|
||||
{file = "mypy-0.991-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb"},
|
||||
{file = "mypy-0.991-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305"},
|
||||
{file = "mypy-0.991-cp310-cp310-win_amd64.whl", hash = "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c"},
|
||||
{file = "mypy-0.991-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372"},
|
||||
{file = "mypy-0.991-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f"},
|
||||
{file = "mypy-0.991-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33"},
|
||||
{file = "mypy-0.991-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05"},
|
||||
{file = "mypy-0.991-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad"},
|
||||
{file = "mypy-0.991-cp311-cp311-win_amd64.whl", hash = "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297"},
|
||||
{file = "mypy-0.991-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813"},
|
||||
{file = "mypy-0.991-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711"},
|
||||
{file = "mypy-0.991-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd"},
|
||||
{file = "mypy-0.991-cp37-cp37m-win_amd64.whl", hash = "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef"},
|
||||
{file = "mypy-0.991-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a"},
|
||||
{file = "mypy-0.991-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93"},
|
||||
{file = "mypy-0.991-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf"},
|
||||
{file = "mypy-0.991-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135"},
|
||||
{file = "mypy-0.991-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70"},
|
||||
{file = "mypy-0.991-cp38-cp38-win_amd64.whl", hash = "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243"},
|
||||
{file = "mypy-0.991-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d"},
|
||||
{file = "mypy-0.991-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5"},
|
||||
{file = "mypy-0.991-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3"},
|
||||
{file = "mypy-0.991-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648"},
|
||||
{file = "mypy-0.991-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476"},
|
||||
{file = "mypy-0.991-cp39-cp39-win_amd64.whl", hash = "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461"},
|
||||
{file = "mypy-0.991-py3-none-any.whl", hash = "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb"},
|
||||
{file = "mypy-0.991.tar.gz", hash = "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
summary = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
files = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "22.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Core utilities for Python packages"
|
||||
files = [
|
||||
{file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"},
|
||||
{file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.10.3"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Utility library for gitignore style pattern matching of file paths."
|
||||
files = [
|
||||
{file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"},
|
||||
{file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "2.6.2"
|
||||
requires_python = ">=3.7"
|
||||
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
dependencies = [
|
||||
"typing-extensions>=4.4; python_version < \"3.8\"",
|
||||
]
|
||||
files = [
|
||||
{file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"},
|
||||
{file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.0.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "plugin and hook calling mechanisms for python"
|
||||
dependencies = [
|
||||
"importlib-metadata>=0.12; python_version < \"3.8\"",
|
||||
]
|
||||
files = [
|
||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||
]
|
||||
|
||||
[[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 = "1.1.1"
|
||||
requires_python = ">=3.7.9,<4"
|
||||
summary = "A pythonic generic language server (pronounced like 'pie glass')"
|
||||
dependencies = [
|
||||
"lsprotocol==2023.0.0b1",
|
||||
"typeguard<4.0.0,>=3.0.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pygls-1.1.1-py3-none-any.whl", hash = "sha256:330704551a335b443bf1cdfb0507f121608591095898d451f0007eeb1510067c"},
|
||||
{file = "pygls-1.1.1.tar.gz", hash = "sha256:b1b4ddd6f800a5573f61f0ec2cd3bc7a859d171f48142b46e1de35a1357c00fe"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.2.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "pytest: simple powerful testing with Python"
|
||||
dependencies = [
|
||||
"attrs>=19.2.0",
|
||||
"colorama; sys_platform == \"win32\"",
|
||||
"exceptiongroup>=1.0.0rc8; python_version < \"3.11\"",
|
||||
"importlib-metadata>=0.12; python_version < \"3.8\"",
|
||||
"iniconfig",
|
||||
"packaging",
|
||||
"pluggy<2.0,>=0.12",
|
||||
"tomli>=1.0.0; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"},
|
||||
{file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "4.0.0"
|
||||
requires_python = ">=3.6"
|
||||
summary = "Pytest plugin for measuring coverage."
|
||||
dependencies = [
|
||||
"coverage[toml]>=5.2.1",
|
||||
"pytest>=4.6",
|
||||
]
|
||||
files = [
|
||||
{file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"},
|
||||
{file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-datadir"
|
||||
version = "1.4.1"
|
||||
requires_python = ">=3.6"
|
||||
summary = "pytest plugin for test data directories and files"
|
||||
dependencies = [
|
||||
"pytest>=5.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pytest-datadir-1.4.1.tar.gz", hash = "sha256:9f7a3c4def6ac4cac3cc8181139ab53bd2667231052bd40cb07081748d4420f0"},
|
||||
{file = "pytest_datadir-1.4.1-py3-none-any.whl", hash = "sha256:095f441782b1b907587eca7227fdbae94be43f1c96b4b2cbcc6801a4645be1af"},
|
||||
]
|
||||
|
||||
[[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 = "typed-ast"
|
||||
version = "1.5.4"
|
||||
requires_python = ">=3.6"
|
||||
summary = "a fork of Python 2 and 3 ast modules with type comment support"
|
||||
files = [
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"},
|
||||
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typeguard"
|
||||
version = "3.0.2"
|
||||
requires_python = ">=3.7.4"
|
||||
summary = "Run-time type checker for Python"
|
||||
dependencies = [
|
||||
"importlib-metadata>=3.6; python_version < \"3.10\"",
|
||||
"typing-extensions>=4.4.0; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "typeguard-3.0.2-py3-none-any.whl", hash = "sha256:bbe993854385284ab42fd5bd3bee6f6556577ce8b50696d6cb956d704f286c8e"},
|
||||
{file = "typeguard-3.0.2.tar.gz", hash = "sha256:fee5297fdb28f8e9efcb8142b5ee219e02375509cd77ea9d270b5af826358d5a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.9.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Backported and Experimental Type Hints for Python 3.7+"
|
||||
files = [
|
||||
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
|
||||
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.11.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
files = [
|
||||
{file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"},
|
||||
{file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"},
|
||||
]
|
||||
368
poetry.lock
generated
368
poetry.lock
generated
@@ -1,368 +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 = "Code coverage measurement for Python"
|
||||
name = "coverage"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
version = "5.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Discover and load entry points from installed packages."
|
||||
name = "entrypoints"
|
||||
optional = false
|
||||
python-versions = ">=2.7"
|
||||
version = "0.3"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A platform independent file lock."
|
||||
name = "filelock"
|
||||
optional = false
|
||||
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 measuring coverage."
|
||||
name = "pytest-cov"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "2.8.1"
|
||||
|
||||
[package.dependencies]
|
||||
coverage = ">=4.4"
|
||||
pytest = ">=3.6"
|
||||
|
||||
[[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 = "284b539e6199a16441b6196fcbc38a374c886e328ae0c5e8bf07d0aaa47b0670"
|
||||
python-versions = "^3.6"
|
||||
|
||||
[metadata.hashes]
|
||||
atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"]
|
||||
attrs = ["08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"]
|
||||
colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"]
|
||||
coverage = ["0cd13a6e98c37b510a2d34c8281d5e1a226aaf9b65b7d770ef03c63169965351", "1a4b6b6a2a3a6612e6361130c2cc3dc4378d8c221752b96167ccbad94b47f3cd", "2ee55e6dba516ddf6f484aa83ccabbb0adf45a18892204c23486938d12258cde", "3be5338a2eb4ef03c57f20917e1d12a1fd10e3853fed060b6d6b677cb3745898", "44b783b02db03c4777d8cf71bae19eadc171a6f2a96777d916b2c30a1eb3d070", "475bf7c4252af0a56e1abba9606f1e54127cdf122063095c75ab04f6f99cf45e", "47c81ee687eafc2f1db7f03fbe99aab81330565ebc62fb3b61edfc2216a550c8", "4a7f8e72b18f2aca288ff02255ce32cc830bc04d993efbc87abf6beddc9e56c0", "50197163a22fd17f79086e087a787883b3ec9280a509807daf158dfc2a7ded02", "56b13000acf891f700f5067512b804d1ec8c301d627486c678b903859d07f798", "79388ae29c896299b3567965dbcd93255f175c17c6c7bca38614d12718c47466", "79fd5d3d62238c4f583b75d48d53cdae759fe04d4fb18fe8b371d88ad2b6f8be", "7fe3e2fde2bf1d7ce25ebcd2d3de3650b8d60d9a73ce6dcef36e20191291613d", "81042a24f67b96e4287774014fa27220d8a4d91af1043389e4d73892efc89ac6", "81326f1095c53111f8afc95da281e1414185f4a538609a77ca50bdfa39a6c207", "8873dc0d8f42142ea9f20c27bbdc485190fff93823c6795be661703369e5877d", "88d2cbcb0a112f47eef71eb95460b6995da18e6f8ca50c264585abc2c473154b", "91f2491aeab9599956c45a77c5666d323efdec790bfe23fcceafcd91105d585a", "979daa8655ae5a51e8e7a24e7d34e250ae8309fd9719490df92cbb2fe2b0422b", "9c871b006c878a890c6e44a5b2f3c6291335324b298c904dc0402ee92ee1f0be", "a6d092545e5af53e960465f652e00efbf5357adad177b2630d63978d85e46a72", "b5ed7837b923d1d71c4f587ae1539ccd96bfd6be9788f507dbe94dab5febbb5d", "ba259f68250f16d2444cbbfaddaa0bb20e1560a4fdaad50bece25c199e6af864", "be1d89614c6b6c36d7578496dc8625123bda2ff44f224cf8b1c45b810ee7383f", "c1b030a79749aa8d1f1486885040114ee56933b15ccfc90049ba266e4aa2139f", "c95bb147fab76f2ecde332d972d8f4138b8f2daee6c466af4ff3b4f29bd4c19e", "d52c1c2d7e856cecc05aa0526453cb14574f821b7f413cc279b9514750d795c1", "d609a6d564ad3d327e9509846c2c47f170456344521462b469e5cb39e48ba31c", "e1bad043c12fb58e8c7d92b3d7f2f49977dcb80a08a6d1e7a5114a11bf819fca", "e5a675f6829c53c87d79117a8eb656cc4a5f8918185a32fc93ba09778e90f6db", "fec32646b98baf4a22fdceb08703965bd16dea09051fbeb31a04b5b6e72b846c"]
|
||||
entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"]
|
||||
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-cov = ["cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b", "cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"]
|
||||
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,41 +1,61 @@
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "cmake-language-server"
|
||||
version = "0.1.1"
|
||||
dynamic = ["version"]
|
||||
description = "CMake LSP Implementation"
|
||||
license = "MIT"
|
||||
authors = ["regen"]
|
||||
authors = [
|
||||
{name = "Regen"},
|
||||
]
|
||||
dependencies = [
|
||||
"pygls>=1.1.1",
|
||||
]
|
||||
requires-python = ">=3.8.0,<3.13"
|
||||
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"
|
||||
pytest-cov = "^2.8"
|
||||
|
||||
[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.1'
|
||||
@@ -1,308 +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__)
|
||||
|
||||
|
||||
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]
|
||||
_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:
|
||||
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)],
|
||||
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', str(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()
|
||||
self._parse_modules()
|
||||
|
||||
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+?
|
||||
[\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 = _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:
|
||||
p = subprocess.run([self._cmake, '--help-modules'],
|
||||
stdout=subprocess.PIPE,
|
||||
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 is not None and 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 '...')
|
||||
@@ -1,143 +0,0 @@
|
||||
from typing import List
|
||||
|
||||
from .parser import TokenList
|
||||
|
||||
|
||||
class Formatter(object):
|
||||
indnt: str
|
||||
lower_identifier: bool
|
||||
|
||||
def __init__(self, indent=' ', lower_identifier=True):
|
||||
self.indent = indent
|
||||
self.lower_identifier = lower_identifier
|
||||
|
||||
def format(self, tokens: TokenList) -> str:
|
||||
cmds: List[str] = ['']
|
||||
indnet_level = 0
|
||||
for token in tokens:
|
||||
if isinstance(token, tuple):
|
||||
raw_identifier = token[0]
|
||||
identifier = raw_identifier.lower()
|
||||
if identifier in ('elseif', 'else', 'endif', 'endforeach',
|
||||
'endwhile', 'endmacro', 'endfunction'):
|
||||
if indnet_level > 0:
|
||||
indnet_level -= 1
|
||||
cmds[-1] = self.indent * indnet_level
|
||||
cmds[-1] += (identifier
|
||||
if self.lower_identifier else raw_identifier)
|
||||
args = self._format_args(token[1])
|
||||
if len(args) < 2:
|
||||
cmds[-1] += '(' + ''.join(args) + ')'
|
||||
else:
|
||||
cmds[-1] += '(\n'
|
||||
for arg in args:
|
||||
cmds[-1] += self.indent * (indnet_level +
|
||||
1) + arg + '\n'
|
||||
cmds[-1] += self.indent * indnet_level + ')'
|
||||
if identifier in ('if', 'elseif', 'else', 'foreach', 'while',
|
||||
'macro', 'function'):
|
||||
indnet_level += 1
|
||||
elif token == '\n':
|
||||
cmds.append('')
|
||||
elif token[0] == '#':
|
||||
if cmds[-1]:
|
||||
cmds[-1] += token
|
||||
else:
|
||||
cmds[-1] = self.indent * indnet_level + token
|
||||
elif cmds[-1]:
|
||||
cmds[-1] += token
|
||||
|
||||
cmds = self._strip_line(cmds)
|
||||
return '\n'.join(cmds) + '\n'
|
||||
|
||||
def _format_args(self, args: List[str]) -> List[str]:
|
||||
lines = ['']
|
||||
for i in range(len(args)):
|
||||
arg = args[i]
|
||||
if arg[0] == '#':
|
||||
lines[-1] += arg
|
||||
elif arg[0] == '\n':
|
||||
lines.append('')
|
||||
elif arg.isspace():
|
||||
if lines[-1]:
|
||||
if i + 1 < len(args) and args[i + 1][0] == '#':
|
||||
lines[-1] += arg
|
||||
else:
|
||||
lines[-1] += ' '
|
||||
else:
|
||||
lines[-1] += arg
|
||||
|
||||
return self._strip_line(lines)
|
||||
|
||||
def _strip_line(self, lines: List[str]) -> List[str]:
|
||||
'''Delete empty lines at the start/end of the input'''
|
||||
|
||||
ret: List[str] = []
|
||||
for line in lines:
|
||||
line = line.rstrip()
|
||||
if line != '' or len(ret) > 0:
|
||||
ret.append(line)
|
||||
while ret and ret[-1] == '':
|
||||
del ret[-1]
|
||||
return ret
|
||||
|
||||
|
||||
def main(args: List[str] = None):
|
||||
from argparse import ArgumentParser
|
||||
from difflib import unified_diff
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from . import __version__
|
||||
from .parser import ListParser
|
||||
|
||||
parser = ArgumentParser(
|
||||
description='Format CMake list files.',
|
||||
epilog='''
|
||||
If no arguments are specified, it formats the code from
|
||||
standard input and writes the result to the standard output.''',
|
||||
)
|
||||
parser.add_argument('lists', type=Path, nargs='*', help='CMake list files')
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument('-i',
|
||||
'--inplace',
|
||||
action='store_true',
|
||||
help='inplace edit')
|
||||
group.add_argument('-d', '--diff', action='store_true', help='show diff')
|
||||
parser.add_argument('--version',
|
||||
action='version',
|
||||
version=f'%(prog)s {__version__}')
|
||||
|
||||
args = parser.parse_args(args)
|
||||
|
||||
if not args.lists and args.inplace:
|
||||
print('error: cannot use -i when no arguments are specified.',
|
||||
file=sys.stderr)
|
||||
return
|
||||
if not args.lists:
|
||||
args.lists.append(None)
|
||||
|
||||
list_parser = ListParser()
|
||||
formatter = Formatter()
|
||||
for listpath in args.lists:
|
||||
if listpath is None:
|
||||
listpath = '(stdin)'
|
||||
content = sys.stdin.read()
|
||||
else:
|
||||
with listpath.open() as fp:
|
||||
content = fp.read()
|
||||
tokens, remain = list_parser.parse(content)
|
||||
formatted = content if remain else formatter.format(tokens)
|
||||
|
||||
if args.inplace:
|
||||
if not remain:
|
||||
with listpath.open('w') as fp:
|
||||
fp.write(formatted)
|
||||
elif args.diff:
|
||||
diff = unified_diff(content.splitlines(True),
|
||||
formatted.splitlines(True), str(listpath),
|
||||
str(listpath), '(before formatting)',
|
||||
'(after formatting)')
|
||||
diffstr = ''.join(diff)
|
||||
print(diffstr, end='')
|
||||
else:
|
||||
print(formatted, end='')
|
||||
@@ -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,178 +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, *args):
|
||||
super().__init__(*args)
|
||||
|
||||
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:
|
||||
word = self._cursor_word(params.textDocument.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(x,
|
||||
CompletionItemKind.Function,
|
||||
documentation=self._api.get_command_doc(x))
|
||||
for x in commands)
|
||||
|
||||
if trigger is None or trigger == '{':
|
||||
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 is None:
|
||||
targets = self._api.search_target(token)
|
||||
items.extend(
|
||||
CompletionItem(x, CompletionItemKind.Class)
|
||||
for x in targets)
|
||||
|
||||
if trigger == '(':
|
||||
func = self._cursor_function(params.textDocument.uri,
|
||||
params.position)
|
||||
if func is not None:
|
||||
func = func.lower()
|
||||
if func == 'include':
|
||||
modules = self._api.search_module(token, False)
|
||||
items.extend(
|
||||
CompletionItem(x,
|
||||
CompletionItemKind.Module,
|
||||
documentation=self._api.
|
||||
get_module_doc(x, False))
|
||||
for x in modules)
|
||||
elif func == 'find_package':
|
||||
modules = self._api.search_module(token, True)
|
||||
items.extend(
|
||||
CompletionItem(x,
|
||||
CompletionItemKind.Module,
|
||||
documentation=self._api.
|
||||
get_module_doc(x, True))
|
||||
for x in modules)
|
||||
|
||||
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):
|
||||
word = self._cursor_word(params.textDocument.uri, params.position,
|
||||
True)
|
||||
if not word:
|
||||
return None
|
||||
|
||||
candidates = [
|
||||
lambda x: self._api.get_command_doc(x.lower()),
|
||||
lambda x: self._api.get_variable_doc(x),
|
||||
lambda x: self._api.get_module_doc(x, False),
|
||||
lambda x: self._api.get_module_doc(x, True),
|
||||
]
|
||||
for c in candidates:
|
||||
doc = c(word[0])
|
||||
if doc is None:
|
||||
continue
|
||||
return Hover(MarkupContent(MarkupKind.Markdown, doc), word[1])
|
||||
return None
|
||||
|
||||
@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_function(self, uri: str, position: Position) -> Optional[str]:
|
||||
doc = self.workspace.get_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_document(uri)
|
||||
content = doc.source
|
||||
line = content.split('\n')[position.line]
|
||||
return 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(Position(position.line, m.start()),
|
||||
Position(position.line, end)))
|
||||
return word
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logging.getLogger('pygls').setLevel(logging.WARNING)
|
||||
CMakeLanguageServer().start_io()
|
||||
@@ -1,59 +1,60 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import pprint
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE, run
|
||||
from threading import Thread
|
||||
from typing import Iterable, Tuple
|
||||
|
||||
import pytest
|
||||
from pygls import features
|
||||
from lsprotocol.types import EXIT, SHUTDOWN
|
||||
from pygls.server import LanguageServer
|
||||
|
||||
from cmake_language_server.server import CMakeLanguageServer
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def cmake_build(shared_datadir):
|
||||
source = shared_datadir / 'cmake'
|
||||
build = source / 'build'
|
||||
def cmake_build(shared_datadir: Path) -> Iterable[Path]:
|
||||
source = shared_datadir / "cmake"
|
||||
build = source / "build"
|
||||
build.mkdir()
|
||||
p = run(['cmake', str(source)],
|
||||
cwd=build,
|
||||
stdout=PIPE,
|
||||
stderr=PIPE,
|
||||
universal_newlines=True)
|
||||
p = run(
|
||||
["cmake", str(source)],
|
||||
cwd=build,
|
||||
stdout=PIPE,
|
||||
stderr=PIPE,
|
||||
universal_newlines=True,
|
||||
)
|
||||
if p.returncode != 0:
|
||||
logging.error('env:\n' + pprint.pformat(os.environ))
|
||||
logging.error('stdout:\n' + p.stdout)
|
||||
logging.error('stderr:\n' + p.stderr)
|
||||
logging.error("stdout:\n" + p.stdout)
|
||||
logging.error("stderr:\n" + p.stderr)
|
||||
raise RuntimeError("CMake failed")
|
||||
yield build
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client_server():
|
||||
def client_server() -> Iterable[Tuple[LanguageServer, CMakeLanguageServer]]:
|
||||
c2s_r, c2s_w = os.pipe()
|
||||
s2c_r, s2c_w = os.pipe()
|
||||
|
||||
def start(ls: LanguageServer, fdr, fdw):
|
||||
# TODO: better patch is needed
|
||||
# disable `close()` to avoid error messages
|
||||
close = ls.loop.close
|
||||
ls.loop.close = lambda: None
|
||||
ls.start_io(os.fdopen(fdr, 'rb'), os.fdopen(fdw, 'wb'))
|
||||
ls.loop.close = close
|
||||
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(asyncio.new_event_loop())
|
||||
server = CMakeLanguageServer("server", "v1")
|
||||
server_thread = Thread(target=start, args=(server, c2s_r, s2c_w))
|
||||
server_thread.start()
|
||||
|
||||
client = LanguageServer(asyncio.new_event_loop())
|
||||
client = LanguageServer("client", "v1")
|
||||
client_thread = Thread(target=start, args=(client, s2c_r, c2s_w))
|
||||
client_thread.start()
|
||||
|
||||
yield client, server
|
||||
|
||||
client.send_notification(features.EXIT)
|
||||
server.send_notification(features.EXIT)
|
||||
server_thread.join()
|
||||
# fix bug on python 3.7
|
||||
if hasattr(client.loop, "_signal_handlers"):
|
||||
client.loop._signal_handlers.clear()
|
||||
|
||||
client.lsp.send_request(SHUTDOWN) # type:ignore[no-untyped-call]
|
||||
client.lsp.notify(EXIT)
|
||||
client_thread.join()
|
||||
server_thread.join()
|
||||
|
||||
@@ -1,110 +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()
|
||||
|
||||
import platform
|
||||
|
||||
system = platform.system()
|
||||
if system == 'Linux':
|
||||
assert 'GNU' in api.get_variable_doc('CMAKE_CXX_COMPILER_ID')
|
||||
elif system == 'Windows':
|
||||
assert 'MSVC' in api.get_variable_doc('CMAKE_CXX_COMPILER_ID')
|
||||
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')
|
||||
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):
|
||||
api = API('cmake', cmake_build)
|
||||
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')
|
||||
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'
|
||||
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(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']
|
||||
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,156 +0,0 @@
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from io import StringIO
|
||||
|
||||
from cmake_language_server.formatter import Formatter, main
|
||||
from cmake_language_server.parser import ListParser
|
||||
|
||||
|
||||
def make_formatter_test(liststr: str, expect: str):
|
||||
def test():
|
||||
tokens, remain = ListParser().parse(liststr)
|
||||
actual = Formatter().format(tokens)
|
||||
assert actual == expect
|
||||
|
||||
return test
|
||||
|
||||
|
||||
test_command = make_formatter_test('a()', 'a()\n')
|
||||
test_command_tolower = make_formatter_test('A()', 'a()\n')
|
||||
test_remove_space = make_formatter_test('''
|
||||
#a
|
||||
b ( c ) # d
|
||||
''', '''\
|
||||
#a
|
||||
b(c) # d
|
||||
''')
|
||||
test_indent_if = make_formatter_test(
|
||||
'''
|
||||
if()
|
||||
a() # a
|
||||
else()
|
||||
# b
|
||||
b()
|
||||
endif()
|
||||
''', '''\
|
||||
if()
|
||||
a() # a
|
||||
else()
|
||||
# b
|
||||
b()
|
||||
endif()
|
||||
''')
|
||||
test_indent_if_nested = make_formatter_test(
|
||||
'''
|
||||
if()
|
||||
if()
|
||||
a()
|
||||
b()
|
||||
endif()
|
||||
endif()
|
||||
''', '''\
|
||||
if()
|
||||
if()
|
||||
a()
|
||||
b()
|
||||
endif()
|
||||
endif()
|
||||
''')
|
||||
test_argument = make_formatter_test('a( b c d)', 'a(b c d)\n')
|
||||
test_argument_multiline = make_formatter_test(
|
||||
'''
|
||||
if()
|
||||
a(b c
|
||||
d # e
|
||||
f
|
||||
# g
|
||||
) # h
|
||||
endif()
|
||||
''', '''\
|
||||
if()
|
||||
a(
|
||||
b c
|
||||
d # e
|
||||
f
|
||||
# g
|
||||
) # h
|
||||
endif()
|
||||
''')
|
||||
|
||||
|
||||
@contextmanager
|
||||
def mock_stdin(buf: str):
|
||||
stdin = sys.stdin
|
||||
sys.stdin = StringIO(buf)
|
||||
yield
|
||||
sys.stdin = stdin
|
||||
|
||||
|
||||
def test_main_stdin(capsys):
|
||||
with mock_stdin(' a()'):
|
||||
main([])
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == 'a()\n'
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_main_stdin_diff(capsys):
|
||||
with mock_stdin(' a()'):
|
||||
main(['-d'])
|
||||
captured = capsys.readouterr()
|
||||
assert '- a()' in captured.out
|
||||
assert '+a()' in captured.out
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_main_file_1(capsys, tmp_path):
|
||||
testfile1 = tmp_path / 'list1.cmake'
|
||||
with testfile1.open('w') as fp:
|
||||
fp.write(' a()')
|
||||
|
||||
main([str(testfile1)])
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == 'a()\n'
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_main_file_2(capsys, tmp_path):
|
||||
testfile1 = tmp_path / 'list1.cmake'
|
||||
with testfile1.open('w') as fp:
|
||||
fp.write(' a()')
|
||||
testfile2 = tmp_path / 'list2.cmake'
|
||||
with testfile2.open('w') as fp:
|
||||
fp.write(' b()')
|
||||
|
||||
main([str(testfile1), str(testfile2)])
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == 'a()\nb()\n'
|
||||
assert captured.err == ''
|
||||
|
||||
|
||||
def test_main_inplace(capsys, tmp_path):
|
||||
testfile1 = tmp_path / 'list1.cmake'
|
||||
with testfile1.open('w') as fp:
|
||||
fp.write(' a()')
|
||||
|
||||
main(['-i', str(testfile1)])
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == ''
|
||||
assert captured.err == ''
|
||||
|
||||
with testfile1.open() as fp:
|
||||
content = fp.read()
|
||||
assert content == 'a()\n'
|
||||
|
||||
|
||||
def test_main_diff(capsys, tmp_path):
|
||||
testfile1 = tmp_path / 'list1.cmake'
|
||||
with testfile1.open('w') as fp:
|
||||
fp.write(' a()')
|
||||
|
||||
main(['-d', str(testfile1)])
|
||||
captured = capsys.readouterr()
|
||||
assert str(testfile1) in captured.out
|
||||
assert '- a()' in captured.out
|
||||
assert '+a()' in captured.out
|
||||
assert captured.err == ''
|
||||
@@ -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')
|
||||
@@ -1,46 +1,93 @@
|
||||
from concurrent import futures
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from pygls.features import (COMPLETION, FORMATTING, HOVER, INITIALIZE,
|
||||
TEXT_DOCUMENT_DID_OPEN)
|
||||
import pytest
|
||||
from lsprotocol.types import (
|
||||
INITIALIZE,
|
||||
TEXT_DOCUMENT_COMPLETION,
|
||||
TEXT_DOCUMENT_DID_OPEN,
|
||||
TEXT_DOCUMENT_FORMATTING,
|
||||
TEXT_DOCUMENT_HOVER,
|
||||
ClientCapabilities,
|
||||
CompletionContext,
|
||||
CompletionList,
|
||||
CompletionParams,
|
||||
CompletionTriggerKind,
|
||||
DidOpenTextDocumentParams,
|
||||
DocumentFormattingParams,
|
||||
FormattingOptions,
|
||||
HoverParams,
|
||||
InitializeParams,
|
||||
MarkupContent,
|
||||
Position,
|
||||
TextDocumentIdentifier,
|
||||
TextDocumentItem,
|
||||
)
|
||||
from pygls.server import LanguageServer
|
||||
from pygls.types import (CompletionContext, CompletionParams,
|
||||
CompletionTriggerKind, DidOpenTextDocumentParams,
|
||||
DocumentFormattingParams, FormattingOptions,
|
||||
InitializeParams, Position, TextDocumentIdentifier,
|
||||
TextDocumentItem, TextDocumentPositionParams)
|
||||
|
||||
from cmake_language_server.server import CMakeLanguageServer
|
||||
|
||||
CALL_TIMEOUT = 2
|
||||
|
||||
|
||||
def _init(client: LanguageServer, root: Path):
|
||||
def _init(client: LanguageServer, root: Path) -> None:
|
||||
retry = 3
|
||||
while retry > 0:
|
||||
try:
|
||||
client.lsp.send_request(
|
||||
client.lsp.send_request( # type:ignore[no-untyped-call]
|
||||
INITIALIZE,
|
||||
InitializeParams(
|
||||
process_id=1234, root_uri=root.as_uri(),
|
||||
capabilities=None)).result(timeout=CALL_TIMEOUT)
|
||||
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):
|
||||
def _open(client: LanguageServer, path: Path, text: Optional[str] = None) -> None:
|
||||
if text is None:
|
||||
with open(path) as fp:
|
||||
text = fp.read()
|
||||
text = path.read_text()
|
||||
|
||||
client.lsp.notify(
|
||||
TEXT_DOCUMENT_DID_OPEN,
|
||||
DidOpenTextDocumentParams(
|
||||
TextDocumentItem(path.as_uri(), 'cmake', 1, text)))
|
||||
text_document=TextDocumentItem(
|
||||
uri=path.as_uri(), language_id="cmake", version=1, text=text
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_initialize(client_server, datadir):
|
||||
def _test_completion(
|
||||
client_server: Tuple[LanguageServer, CMakeLanguageServer],
|
||||
datadir: Path,
|
||||
content: str,
|
||||
context: Optional[CompletionContext],
|
||||
) -> 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.lsp.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
|
||||
@@ -48,83 +95,75 @@ def test_initialize(client_server, datadir):
|
||||
assert server._api is not None
|
||||
|
||||
|
||||
def test_completions_invoked(client_server, datadir):
|
||||
client, server = client_server
|
||||
_init(client, datadir)
|
||||
path = datadir / 'CMakeLists.txt'
|
||||
_open(client, path, 'projec')
|
||||
response = client.lsp.send_request(
|
||||
COMPLETION,
|
||||
CompletionParams(TextDocumentIdentifier(path.as_uri()), Position(
|
||||
0, 6), CompletionContext(
|
||||
CompletionTriggerKind.Invoked))).result(timeout=CALL_TIMEOUT)
|
||||
item = next(filter(lambda x: x.label == 'project', response.items), None)
|
||||
@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 '<PROJECT-NAME>' in item.documentation
|
||||
assert isinstance(item.documentation, MarkupContent)
|
||||
assert "<PROJECT-NAME>" in item.documentation.value
|
||||
|
||||
|
||||
def test_completions_triggercharacter_variable(client_server, datadir):
|
||||
@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, '${')
|
||||
response = client.lsp.send_request(
|
||||
COMPLETION,
|
||||
CompletionParams(
|
||||
TextDocumentIdentifier(path.as_uri()), Position(0, 2),
|
||||
CompletionContext(CompletionTriggerKind.TriggerCharacter,
|
||||
'{'))).result(timeout=CALL_TIMEOUT)
|
||||
assert 'PROJECT_VERSION' in [x.label for x in response.items]
|
||||
path = datadir / "CMakeLists.txt"
|
||||
_open(client, path, "a ( b c ) ")
|
||||
response = client.lsp.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_completions_triggercharacter_module(client_server, datadir):
|
||||
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, 'include(')
|
||||
response = client.lsp.send_request(
|
||||
COMPLETION,
|
||||
CompletionParams(
|
||||
TextDocumentIdentifier(path.as_uri()), Position(0, 8),
|
||||
CompletionContext(CompletionTriggerKind.TriggerCharacter,
|
||||
'('))).result(timeout=CALL_TIMEOUT)
|
||||
assert 'GoogleTest' in [x.label for x in response.items]
|
||||
|
||||
|
||||
def test_completions_triggercharacter_package(client_server, datadir):
|
||||
client, server = client_server
|
||||
_init(client, datadir)
|
||||
path = datadir / 'CMakeLists.txt'
|
||||
_open(client, path, 'find_package(')
|
||||
response = client.lsp.send_request(
|
||||
COMPLETION,
|
||||
CompletionParams(
|
||||
TextDocumentIdentifier(path.as_uri()), Position(0, 13),
|
||||
CompletionContext(CompletionTriggerKind.TriggerCharacter,
|
||||
'('))).result(timeout=CALL_TIMEOUT)
|
||||
assert 'Boost' in [x.label for x in response.items]
|
||||
|
||||
|
||||
def test_formatting(client_server, datadir):
|
||||
client, server = client_server
|
||||
_init(client, datadir)
|
||||
path = datadir / 'CMakeLists.txt'
|
||||
_open(client, path, 'a ( b c ) ')
|
||||
response = client.lsp.send_request(
|
||||
FORMATTING,
|
||||
DocumentFormattingParams(TextDocumentIdentifier(path.as_uri()),
|
||||
FormattingOptions(
|
||||
2, True))).result(timeout=CALL_TIMEOUT)
|
||||
assert response[0].newText == 'a(b c)\n'
|
||||
|
||||
|
||||
def test_hover(client_server, datadir):
|
||||
client, server = client_server
|
||||
_init(client, datadir)
|
||||
path = datadir / 'CMakeLists.txt'
|
||||
_open(client, path, 'project()')
|
||||
response = client.lsp.send_request(
|
||||
HOVER,
|
||||
TextDocumentPositionParams(TextDocumentIdentifier(path.as_uri()),
|
||||
Position())).result(timeout=CALL_TIMEOUT)
|
||||
assert '<PROJECT-NAME>' in response.contents.value
|
||||
path = datadir / "CMakeLists.txt"
|
||||
_open(client, path, "project()")
|
||||
response = client.lsp.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
|
||||
|
||||
30
tox.ini
30
tox.ini
@@ -1,26 +1,22 @@
|
||||
[tox]
|
||||
env_list = py{38,39,310,311,312}
|
||||
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
|
||||
|
||||
[testenv]
|
||||
whitelist_externals = poetry
|
||||
skip_install = true
|
||||
passenv = INCLUDE LIB LIBPATH Platform VCTools* VSCMD_* WindowsSDK*
|
||||
commands_pre =
|
||||
poetry install
|
||||
allowlist_externals =
|
||||
pdm
|
||||
commands =
|
||||
poetry run pytest --cov-report=term --cov-report=xml --cov=src -sv tests
|
||||
|
||||
[testenv:lint]
|
||||
commands =
|
||||
poetry run isort -c -rc src tests
|
||||
poetry run yapf -d -r src tests
|
||||
poetry run flake8 src tests
|
||||
poetry run mypy src tests
|
||||
pdm install --dev -G :all
|
||||
pdm run lint
|
||||
pdm run test
|
||||
|
||||
Reference in New Issue
Block a user