Replace linter (#37)
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
poetry.lock linguist-generated=true
|
||||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python: [3.6, 3.7, 3.8]
|
python: [3.6, 3.7, 3.8, 3.9]
|
||||||
os: [ubuntu-18.04, windows-2016]
|
os: [ubuntu-18.04, windows-2016]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|||||||
129
.gitignore
vendored
129
.gitignore
vendored
@@ -1,133 +1,4 @@
|
|||||||
### https://raw.github.com/github/gitignore/cb0c6ef7ac68f2300409ee85501d9ad432cb4c7e/Python.gitignore
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
pip-wheel-metadata/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
.tox/
|
||||||
.nox/
|
|
||||||
.coverage
|
.coverage
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
coverage.xml
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
.python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# pyflow
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
[style]
|
|
||||||
based_on_style = pep8
|
|
||||||
6
mypy.ini
6
mypy.ini
@@ -1,6 +0,0 @@
|
|||||||
[mypy]
|
|
||||||
ignore_missing_imports = True
|
|
||||||
allow_redefinition = True
|
|
||||||
|
|
||||||
[mypy-re.Scanner]
|
|
||||||
ignore_errors = True
|
|
||||||
754
poetry.lock
generated
754
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -23,14 +23,10 @@ pygls = "^0.8.1"
|
|||||||
pyparsing = "^2.4"
|
pyparsing = "^2.4"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
flake8 = "^3.7"
|
pytest = "^6.2"
|
||||||
mypy = "^0.740.0"
|
|
||||||
pytest = "^5.2"
|
|
||||||
pytest-datadir = "^1.3"
|
pytest-datadir = "^1.3"
|
||||||
tox = "^3.14"
|
pytest-cov = "^2.11"
|
||||||
isort = "^4.3"
|
pysen = {version = "^0.9", extras = ["lint"]}
|
||||||
pytest-cov = "^2.8"
|
|
||||||
black = "^19.10b0"
|
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
cmake-format = "cmake_language_server.formatter:main"
|
cmake-format = "cmake_language_server.formatter:main"
|
||||||
@@ -39,3 +35,18 @@ cmake-language-server = "cmake_language_server.server:main"
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.pysen]
|
||||||
|
version = "0.9"
|
||||||
|
|
||||||
|
[tool.pysen.lint]
|
||||||
|
enable_black = true
|
||||||
|
enable_flake8 = true
|
||||||
|
enable_isort = true
|
||||||
|
enable_mypy = true
|
||||||
|
mypy_preset = "strict"
|
||||||
|
line_length = 88
|
||||||
|
py_version = "py36"
|
||||||
|
|
||||||
|
[[tool.pysen.lint.mypy_targets]]
|
||||||
|
paths = ["."]
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class API(object):
|
|||||||
_uuid: uuid.UUID
|
_uuid: uuid.UUID
|
||||||
_builtin_commands: Dict[str, str]
|
_builtin_commands: Dict[str, str]
|
||||||
_builtin_variables: Dict[str, str]
|
_builtin_variables: Dict[str, str]
|
||||||
_builtin_variable_template: Dict[Pattern, str]
|
_builtin_variable_template: Dict[Pattern[str], str]
|
||||||
_builtin_modules: Dict[str, str]
|
_builtin_modules: Dict[str, str]
|
||||||
_targets: List[str]
|
_targets: List[str]
|
||||||
_cached_variables: Dict[str, str]
|
_cached_variables: Dict[str, str]
|
||||||
@@ -45,11 +45,11 @@ class API(object):
|
|||||||
self._generated_list_parsed = False
|
self._generated_list_parsed = False
|
||||||
|
|
||||||
def query(self) -> bool:
|
def query(self) -> bool:
|
||||||
""" Use CMake's file API to get JSON information about the build tree
|
"""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
|
Generates a JSON request file for the current build tree and runs
|
||||||
CMake on the build tree. Deletes the request file immediately
|
CMake on the build tree. Deletes the request file immediately
|
||||||
after.
|
after.
|
||||||
"""
|
"""
|
||||||
if not self.cmake_cache.exists():
|
if not self.cmake_cache.exists():
|
||||||
return False
|
return False
|
||||||
@@ -83,10 +83,10 @@ class API(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def read_reply(self) -> bool:
|
def read_reply(self) -> bool:
|
||||||
""" Reads the CMake file API reply file and updates internal state
|
"""Reads the CMake file API reply file and updates internal state
|
||||||
|
|
||||||
Reads the result of the previous query file and updates
|
Reads the result of the previous query file and updates
|
||||||
the targets, the cache entries and the cmake files.
|
the targets, the cache entries and the cmake files.
|
||||||
"""
|
"""
|
||||||
reply = self._build / ".cmake" / "api" / "v1" / "reply"
|
reply = self._build / ".cmake" / "api" / "v1" / "reply"
|
||||||
indices = sorted(reply.glob("index-*.json"))
|
indices = sorted(reply.glob("index-*.json"))
|
||||||
@@ -112,13 +112,13 @@ class API(object):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _read_codemodel(self, codemodelpath: Path):
|
def _read_codemodel(self, codemodelpath: Path) -> None:
|
||||||
with (codemodelpath).open() as fp:
|
with (codemodelpath).open() as fp:
|
||||||
codemodel = json.load(fp)
|
codemodel = json.load(fp)
|
||||||
config = codemodel["configurations"][0]
|
config = codemodel["configurations"][0]
|
||||||
self._targets[:] = [x["name"] for x in config["targets"]]
|
self._targets[:] = [x["name"] for x in config["targets"]]
|
||||||
|
|
||||||
def _read_cache(self, cachepath: Path):
|
def _read_cache(self, cachepath: Path) -> None:
|
||||||
with cachepath.open() as fp:
|
with cachepath.open() as fp:
|
||||||
cache = json.load(fp)
|
cache = json.load(fp)
|
||||||
self._cached_variables.clear()
|
self._cached_variables.clear()
|
||||||
@@ -134,7 +134,7 @@ class API(object):
|
|||||||
doc.append(f"`{value}`")
|
doc.append(f"`{value}`")
|
||||||
self._cached_variables[name] = "\n\n".join(doc)
|
self._cached_variables[name] = "\n\n".join(doc)
|
||||||
|
|
||||||
def _read_cmake_files(self, jsonpath: Path):
|
def _read_cmake_files(self, jsonpath: Path) -> None:
|
||||||
"""inspect CMake list files that are used during build generation"""
|
"""inspect CMake list files that are used during build generation"""
|
||||||
|
|
||||||
if not self._builtin_variables or self._generated_list_parsed:
|
if not self._builtin_variables or self._generated_list_parsed:
|
||||||
@@ -214,10 +214,10 @@ endforeach()
|
|||||||
self._parse_modules()
|
self._parse_modules()
|
||||||
|
|
||||||
def _parse_commands(self) -> None:
|
def _parse_commands(self) -> None:
|
||||||
""" Load docs for builtin cmake functions
|
"""Load docs for builtin cmake functions
|
||||||
|
|
||||||
Loads the documentation for builtin cmake functions from the result
|
Loads the documentation for builtin cmake functions from the result
|
||||||
of `$ cmake --help-commands`.
|
of `$ cmake --help-commands`.
|
||||||
"""
|
"""
|
||||||
p = subprocess.run(
|
p = subprocess.run(
|
||||||
[self._cmake, "--help-commands"],
|
[self._cmake, "--help-commands"],
|
||||||
@@ -247,10 +247,10 @@ endforeach()
|
|||||||
self._builtin_commands[command] = "```cmake\n" + signature + "\n```"
|
self._builtin_commands[command] = "```cmake\n" + signature + "\n```"
|
||||||
|
|
||||||
def _parse_variables(self) -> None:
|
def _parse_variables(self) -> None:
|
||||||
""" Load docs for builtin cmake variables
|
"""Load docs for builtin cmake variables
|
||||||
|
|
||||||
Loads the documentation for builtin cmake variables from
|
Loads the documentation for builtin cmake variables from
|
||||||
the result of `$ cmake --help-variables`.
|
the result of `$ cmake --help-variables`.
|
||||||
"""
|
"""
|
||||||
p = subprocess.run(
|
p = subprocess.run(
|
||||||
[self._cmake, "--help-variables"],
|
[self._cmake, "--help-variables"],
|
||||||
@@ -286,10 +286,10 @@ endforeach()
|
|||||||
self._builtin_variables[variable] = doc
|
self._builtin_variables[variable] = doc
|
||||||
|
|
||||||
def _parse_modules(self) -> None:
|
def _parse_modules(self) -> None:
|
||||||
""" Loads docs for all modules in the cmake distribution
|
"""Loads docs for all modules in the cmake distribution
|
||||||
|
|
||||||
Loads the documentation for cmake modules included in the
|
Loads the documentation for cmake modules included in the
|
||||||
distribution from the result of `$ cmake --help-modules`.
|
distribution from the result of `$ cmake --help-modules`.
|
||||||
"""
|
"""
|
||||||
p = subprocess.run(
|
p = subprocess.run(
|
||||||
[self._cmake, "--help-modules"],
|
[self._cmake, "--help-modules"],
|
||||||
@@ -316,7 +316,7 @@ endforeach()
|
|||||||
module = match.group("module")
|
module = match.group("module")
|
||||||
header = match.group("header")
|
header = match.group("header")
|
||||||
doc = _tidy_doc(match.group("doc"))
|
doc = _tidy_doc(match.group("doc"))
|
||||||
if header is not None and header != "Overview":
|
if header != "Overview":
|
||||||
doc = ""
|
doc = ""
|
||||||
self._builtin_modules[module] = doc
|
self._builtin_modules[module] = doc
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
|
||||||
from .parser import TokenList
|
from .parser import TokenList
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ class Formatter(object):
|
|||||||
indent: str
|
indent: str
|
||||||
lower_identifier: bool
|
lower_identifier: bool
|
||||||
|
|
||||||
def __init__(self, indent=" ", lower_identifier=True):
|
def __init__(self, indent: str = " ", lower_identifier: bool = True):
|
||||||
self.indent = indent
|
self.indent = indent
|
||||||
self.lower_identifier = lower_identifier
|
self.lower_identifier = lower_identifier
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ class Formatter(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def main(args: List[str] = None):
|
def main(argss: Optional[List[str]] = None) -> None:
|
||||||
import sys
|
import sys
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from difflib import unified_diff
|
from difflib import unified_diff
|
||||||
@@ -117,7 +117,7 @@ def main(args: List[str] = None):
|
|||||||
"--version", action="version", version=f"%(prog)s {__version__}"
|
"--version", action="version", version=f"%(prog)s {__version__}"
|
||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args(args)
|
args = parser.parse_args(argss)
|
||||||
|
|
||||||
if not args.lists and args.inplace:
|
if not args.lists and args.inplace:
|
||||||
print("error: cannot use -i when no arguments are specified.", file=sys.stderr)
|
print("error: cannot use -i when no arguments are specified.", file=sys.stderr)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ TokenList = List[TokenType]
|
|||||||
class ListParser(object):
|
class ListParser(object):
|
||||||
_parser: pp.ParserElement
|
_parser: pp.ParserElement
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
newline = "\n"
|
newline = "\n"
|
||||||
space_plus = pp.Regex("[ \t]+")
|
space_plus = pp.Regex("[ \t]+")
|
||||||
space_star = pp.Optional(space_plus)
|
space_star = pp.Optional(space_plus)
|
||||||
@@ -20,7 +20,7 @@ class ListParser(object):
|
|||||||
|
|
||||||
bracket_content = pp.Forward()
|
bracket_content = pp.Forward()
|
||||||
|
|
||||||
def action_bracket_open(tokens: pp.ParseResults):
|
def action_bracket_open(tokens: pp.ParseResults) -> None:
|
||||||
nonlocal bracket_content
|
nonlocal bracket_content
|
||||||
marker = "]" + "=" * (len(tokens[0]) - 2) + "]"
|
marker = "]" + "=" * (len(tokens[0]) - 2) + "]"
|
||||||
bracket_content <<= pp.SkipTo(marker, include=True)
|
bracket_content <<= pp.SkipTo(marker, include=True)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional, Tuple
|
from typing import Any, Callable, List, Optional, Tuple
|
||||||
|
|
||||||
from pygls.features import (
|
from pygls.features import (
|
||||||
COMPLETION,
|
COMPLETION,
|
||||||
@@ -36,18 +36,18 @@ from .parser import ListParser
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CMakeLanguageServer(LanguageServer):
|
class CMakeLanguageServer(LanguageServer): # type: ignore
|
||||||
_parser: ListParser
|
_parser: ListParser
|
||||||
_api: API
|
_api: Optional[API]
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args: Any) -> None:
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
|
|
||||||
self._parser = ListParser()
|
self._parser = ListParser()
|
||||||
self._api = None
|
self._api = None
|
||||||
|
|
||||||
@self.feature(INITIALIZE)
|
@self.feature(INITIALIZE)
|
||||||
def initialize(params: InitializeParams):
|
def initialize(params: InitializeParams) -> None:
|
||||||
opts = params.initializationOptions
|
opts = params.initializationOptions
|
||||||
|
|
||||||
cmake = getattr(opts, "cmakeExecutable", "cmake")
|
cmake = getattr(opts, "cmakeExecutable", "cmake")
|
||||||
@@ -60,7 +60,9 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
trigger_characters = ["{", "("]
|
trigger_characters = ["{", "("]
|
||||||
|
|
||||||
@self.feature(COMPLETION, trigger_characters=trigger_characters)
|
@self.feature(COMPLETION, trigger_characters=trigger_characters)
|
||||||
def completions(params: CompletionParams):
|
def completions(params: CompletionParams) -> CompletionList:
|
||||||
|
assert self._api is not None
|
||||||
|
|
||||||
if (
|
if (
|
||||||
hasattr(params, "context")
|
hasattr(params, "context")
|
||||||
and params.context.triggerKind == CompletionTriggerKind.TriggerCharacter
|
and params.context.triggerKind == CompletionTriggerKind.TriggerCharacter
|
||||||
@@ -143,7 +145,7 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
return CompletionList(False, items)
|
return CompletionList(False, items)
|
||||||
|
|
||||||
@self.feature(FORMATTING)
|
@self.feature(FORMATTING)
|
||||||
def formatting(params: DocumentFormattingParams):
|
def formatting(params: DocumentFormattingParams) -> Optional[List[TextEdit]]:
|
||||||
doc = self.workspace.get_document(params.textDocument.uri)
|
doc = self.workspace.get_document(params.textDocument.uri)
|
||||||
content = doc.source
|
content = doc.source
|
||||||
tokens, remain = self._parser.parse(content)
|
tokens, remain = self._parser.parse(content)
|
||||||
@@ -156,16 +158,19 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
return [TextEdit(Range(Position(0, 0), Position(lines + 1, 0)), formatted)]
|
return [TextEdit(Range(Position(0, 0), Position(lines + 1, 0)), formatted)]
|
||||||
|
|
||||||
@self.feature(HOVER)
|
@self.feature(HOVER)
|
||||||
def hover(params: TextDocumentPositionParams):
|
def hover(params: TextDocumentPositionParams) -> Optional[Hover]:
|
||||||
|
assert self._api is not None
|
||||||
|
api = self._api
|
||||||
|
|
||||||
word = self._cursor_word(params.textDocument.uri, params.position, True)
|
word = self._cursor_word(params.textDocument.uri, params.position, True)
|
||||||
if not word:
|
if not word:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
candidates = [
|
candidates: List[Callable[[str], Optional[str]]] = [
|
||||||
lambda x: self._api.get_command_doc(x.lower()),
|
lambda x: api.get_command_doc(x.lower()),
|
||||||
lambda x: self._api.get_variable_doc(x),
|
lambda x: api.get_variable_doc(x),
|
||||||
lambda x: self._api.get_module_doc(x, False),
|
lambda x: api.get_module_doc(x, False),
|
||||||
lambda x: self._api.get_module_doc(x, True),
|
lambda x: api.get_module_doc(x, True),
|
||||||
]
|
]
|
||||||
for c in candidates:
|
for c in candidates:
|
||||||
doc = c(word[0])
|
doc = c(word[0])
|
||||||
@@ -177,7 +182,9 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
@self.thread()
|
@self.thread()
|
||||||
@self.feature(TEXT_DOCUMENT_DID_SAVE, includeText=False)
|
@self.feature(TEXT_DOCUMENT_DID_SAVE, includeText=False)
|
||||||
@self.feature(INITIALIZED)
|
@self.feature(INITIALIZED)
|
||||||
def run_cmake(*args):
|
def run_cmake(*args: Any) -> None:
|
||||||
|
assert self._api is not None
|
||||||
|
|
||||||
if self._api.query():
|
if self._api.query():
|
||||||
self._api.read_reply()
|
self._api.read_reply()
|
||||||
|
|
||||||
@@ -192,7 +199,7 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
doc = self.workspace.get_document(uri)
|
doc = self.workspace.get_document(uri)
|
||||||
content = doc.source
|
content = doc.source
|
||||||
line = content.split("\n")[position.line]
|
line = content.split("\n")[position.line]
|
||||||
return line
|
return str(line)
|
||||||
|
|
||||||
def _cursor_word(
|
def _cursor_word(
|
||||||
self, uri: str, position: Position, include_all: bool = True
|
self, uri: str, position: Position, include_all: bool = True
|
||||||
@@ -212,7 +219,7 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def main(args=None):
|
def main() -> None:
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
@@ -221,7 +228,7 @@ def main(args=None):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--version", action="version", version=f"%(prog)s {__version__}"
|
"--version", action="version", version=f"%(prog)s {__version__}"
|
||||||
)
|
)
|
||||||
args = parser.parse_args(args)
|
parser.parse_args()
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logging.getLogger("pygls").setLevel(logging.WARNING)
|
logging.getLogger("pygls").setLevel(logging.WARNING)
|
||||||
|
|||||||
@@ -2,18 +2,19 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
|
from pathlib import Path
|
||||||
from subprocess import PIPE, run
|
from subprocess import PIPE, run
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
from typing import Iterable, Tuple
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from cmake_language_server.server import CMakeLanguageServer
|
||||||
from pygls import features
|
from pygls import features
|
||||||
from pygls.server import LanguageServer
|
from pygls.server import LanguageServer
|
||||||
|
|
||||||
from cmake_language_server.server import CMakeLanguageServer
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def cmake_build(shared_datadir):
|
def cmake_build(shared_datadir: Path) -> Iterable[Path]:
|
||||||
source = shared_datadir / "cmake"
|
source = shared_datadir / "cmake"
|
||||||
build = source / "build"
|
build = source / "build"
|
||||||
build.mkdir()
|
build.mkdir()
|
||||||
@@ -33,11 +34,11 @@ def cmake_build(shared_datadir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def client_server():
|
def client_server() -> Iterable[Tuple[LanguageServer, CMakeLanguageServer]]:
|
||||||
c2s_r, c2s_w = os.pipe()
|
c2s_r, c2s_w = os.pipe()
|
||||||
s2c_r, s2c_w = os.pipe()
|
s2c_r, s2c_w = os.pipe()
|
||||||
|
|
||||||
def start(ls: LanguageServer, fdr, fdw):
|
def start(ls: LanguageServer, fdr: int, fdw: int) -> None:
|
||||||
# TODO: better patch is needed
|
# TODO: better patch is needed
|
||||||
# disable `close()` to avoid error messages
|
# disable `close()` to avoid error messages
|
||||||
close = ls.loop.close
|
close = ls.loop.close
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from cmake_language_server.api import API
|
from cmake_language_server.api import API
|
||||||
|
|
||||||
|
|
||||||
def test_query_with_cache(cmake_build):
|
def test_query_with_cache(cmake_build: Path) -> None:
|
||||||
api = API("cmake", cmake_build)
|
api = API("cmake", cmake_build)
|
||||||
assert api.query()
|
assert api.query()
|
||||||
|
|
||||||
@@ -14,14 +15,14 @@ def test_query_with_cache(cmake_build):
|
|||||||
assert reply.exists()
|
assert reply.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_query_without_cache(cmake_build):
|
def test_query_without_cache(cmake_build: Path) -> None:
|
||||||
api = API("cmake", cmake_build)
|
api = API("cmake", cmake_build)
|
||||||
(cmake_build / "CMakeCache.txt").unlink()
|
(cmake_build / "CMakeCache.txt").unlink()
|
||||||
|
|
||||||
assert not api.query()
|
assert not api.query()
|
||||||
|
|
||||||
|
|
||||||
def test_read_variable(cmake_build):
|
def test_read_variable(cmake_build: Path) -> None:
|
||||||
api = API("cmake", cmake_build)
|
api = API("cmake", cmake_build)
|
||||||
assert api.query()
|
assert api.query()
|
||||||
assert api.read_reply()
|
assert api.read_reply()
|
||||||
@@ -29,7 +30,7 @@ def test_read_variable(cmake_build):
|
|||||||
assert api.get_variable_doc("testproject_BINARY_DIR")
|
assert api.get_variable_doc("testproject_BINARY_DIR")
|
||||||
|
|
||||||
|
|
||||||
def test_read_cmake_files(cmake_build):
|
def test_read_cmake_files(cmake_build: Path) -> None:
|
||||||
api = API("cmake", cmake_build)
|
api = API("cmake", cmake_build)
|
||||||
api.parse_doc()
|
api.parse_doc()
|
||||||
assert api.query()
|
assert api.query()
|
||||||
@@ -48,7 +49,7 @@ def test_read_cmake_files(cmake_build):
|
|||||||
raise RuntimeError("Unexpected system")
|
raise RuntimeError("Unexpected system")
|
||||||
|
|
||||||
|
|
||||||
def test_parse_commands(cmake_build):
|
def test_parse_commands(cmake_build: Path) -> None:
|
||||||
api = API("cmake", cmake_build)
|
api = API("cmake", cmake_build)
|
||||||
api.parse_doc()
|
api.parse_doc()
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ def test_parse_commands(cmake_build):
|
|||||||
assert api.get_command_doc("not_existing_command") is None
|
assert api.get_command_doc("not_existing_command") is None
|
||||||
|
|
||||||
|
|
||||||
def test_parse_variables(cmake_build):
|
def test_parse_variables(cmake_build: Path) -> None:
|
||||||
api = API("cmake", cmake_build)
|
api = API("cmake", cmake_build)
|
||||||
api.parse_doc()
|
api.parse_doc()
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@ def test_parse_variables(cmake_build):
|
|||||||
assert api.get_variable_doc("not_existing_variable") is None
|
assert api.get_variable_doc("not_existing_variable") is None
|
||||||
|
|
||||||
|
|
||||||
def test_parse_modules(cmake_build):
|
def test_parse_modules(cmake_build: Path) -> None:
|
||||||
api = API("cmake", cmake_build)
|
api = API("cmake", cmake_build)
|
||||||
api.parse_doc()
|
api.parse_doc()
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Callable, Iterator
|
||||||
|
|
||||||
from cmake_language_server.formatter import Formatter, main
|
from cmake_language_server.formatter import Formatter, main
|
||||||
from cmake_language_server.parser import ListParser
|
from cmake_language_server.parser import ListParser
|
||||||
|
from pytest import CaptureFixture
|
||||||
|
|
||||||
|
|
||||||
def make_formatter_test(liststr: str, expect: str):
|
def make_formatter_test(liststr: str, expect: str) -> Callable[[], None]:
|
||||||
def test():
|
def test() -> None:
|
||||||
tokens, remain = ListParser().parse(liststr)
|
tokens, remain = ListParser().parse(liststr)
|
||||||
actual = Formatter().format(tokens)
|
actual = Formatter().format(tokens)
|
||||||
assert actual == expect
|
assert actual == expect
|
||||||
@@ -88,14 +91,14 @@ endif()
|
|||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def mock_stdin(buf: str):
|
def mock_stdin(buf: str) -> Iterator[None]:
|
||||||
stdin = sys.stdin
|
stdin = sys.stdin
|
||||||
sys.stdin = StringIO(buf)
|
sys.stdin = StringIO(buf)
|
||||||
yield
|
yield
|
||||||
sys.stdin = stdin
|
sys.stdin = stdin
|
||||||
|
|
||||||
|
|
||||||
def test_main_stdin(capsys):
|
def test_main_stdin(capsys: CaptureFixture[str]) -> None:
|
||||||
with mock_stdin(" a()"):
|
with mock_stdin(" a()"):
|
||||||
main([])
|
main([])
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
@@ -103,7 +106,7 @@ def test_main_stdin(capsys):
|
|||||||
assert captured.err == ""
|
assert captured.err == ""
|
||||||
|
|
||||||
|
|
||||||
def test_main_stdin_diff(capsys):
|
def test_main_stdin_diff(capsys: CaptureFixture[str]) -> None:
|
||||||
with mock_stdin(" a()"):
|
with mock_stdin(" a()"):
|
||||||
main(["-d"])
|
main(["-d"])
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
@@ -112,7 +115,7 @@ def test_main_stdin_diff(capsys):
|
|||||||
assert captured.err == ""
|
assert captured.err == ""
|
||||||
|
|
||||||
|
|
||||||
def test_main_file_1(capsys, tmp_path):
|
def test_main_file_1(capsys: CaptureFixture[str], tmp_path: Path) -> None:
|
||||||
testfile1 = tmp_path / "list1.cmake"
|
testfile1 = tmp_path / "list1.cmake"
|
||||||
with testfile1.open("w") as fp:
|
with testfile1.open("w") as fp:
|
||||||
fp.write(" a()")
|
fp.write(" a()")
|
||||||
@@ -123,7 +126,7 @@ def test_main_file_1(capsys, tmp_path):
|
|||||||
assert captured.err == ""
|
assert captured.err == ""
|
||||||
|
|
||||||
|
|
||||||
def test_main_file_2(capsys, tmp_path):
|
def test_main_file_2(capsys: CaptureFixture[str], tmp_path: Path) -> None:
|
||||||
testfile1 = tmp_path / "list1.cmake"
|
testfile1 = tmp_path / "list1.cmake"
|
||||||
with testfile1.open("w") as fp:
|
with testfile1.open("w") as fp:
|
||||||
fp.write(" a()")
|
fp.write(" a()")
|
||||||
@@ -137,7 +140,7 @@ def test_main_file_2(capsys, tmp_path):
|
|||||||
assert captured.err == ""
|
assert captured.err == ""
|
||||||
|
|
||||||
|
|
||||||
def test_main_inplace(capsys, tmp_path):
|
def test_main_inplace(capsys: CaptureFixture[str], tmp_path: Path) -> None:
|
||||||
testfile1 = tmp_path / "list1.cmake"
|
testfile1 = tmp_path / "list1.cmake"
|
||||||
with testfile1.open("w") as fp:
|
with testfile1.open("w") as fp:
|
||||||
fp.write(" a()")
|
fp.write(" a()")
|
||||||
@@ -152,7 +155,7 @@ def test_main_inplace(capsys, tmp_path):
|
|||||||
assert content == "a()\n"
|
assert content == "a()\n"
|
||||||
|
|
||||||
|
|
||||||
def test_main_diff(capsys, tmp_path):
|
def test_main_diff(capsys: CaptureFixture[str], tmp_path: Path) -> None:
|
||||||
testfile1 = tmp_path / "list1.cmake"
|
testfile1 = tmp_path / "list1.cmake"
|
||||||
with testfile1.open("w") as fp:
|
with testfile1.open("w") as fp:
|
||||||
fp.write(" a()")
|
fp.write(" a()")
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
from typing import List
|
from typing import Callable, List
|
||||||
|
|
||||||
from cmake_language_server.parser import ListParser, TokenType
|
from cmake_language_server.parser import ListParser, TokenType
|
||||||
|
|
||||||
|
|
||||||
def make_parser_test(
|
def make_parser_test(
|
||||||
liststr: str, expect_token: List[TokenType], expect_remain: str = ""
|
liststr: str, expect_token: List[TokenType], expect_remain: str = ""
|
||||||
):
|
) -> Callable[[], None]:
|
||||||
def test():
|
def test() -> None:
|
||||||
actual_token, actual_remain = ListParser().parse(liststr)
|
actual_token, actual_remain = ListParser().parse(liststr)
|
||||||
assert actual_token == expect_token
|
assert actual_token == expect_token
|
||||||
assert actual_remain == expect_remain
|
assert actual_remain == expect_remain
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from concurrent import futures
|
from concurrent import futures
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
|
from cmake_language_server.server import CMakeLanguageServer
|
||||||
from pygls.features import (
|
from pygls.features import (
|
||||||
COMPLETION,
|
COMPLETION,
|
||||||
FORMATTING,
|
FORMATTING,
|
||||||
@@ -12,6 +13,7 @@ from pygls.features import (
|
|||||||
from pygls.server import LanguageServer
|
from pygls.server import LanguageServer
|
||||||
from pygls.types import (
|
from pygls.types import (
|
||||||
CompletionContext,
|
CompletionContext,
|
||||||
|
CompletionList,
|
||||||
CompletionParams,
|
CompletionParams,
|
||||||
CompletionTriggerKind,
|
CompletionTriggerKind,
|
||||||
DidOpenTextDocumentParams,
|
DidOpenTextDocumentParams,
|
||||||
@@ -27,7 +29,7 @@ from pygls.types import (
|
|||||||
CALL_TIMEOUT = 2
|
CALL_TIMEOUT = 2
|
||||||
|
|
||||||
|
|
||||||
def _init(client: LanguageServer, root: Path):
|
def _init(client: LanguageServer, root: Path) -> None:
|
||||||
retry = 3
|
retry = 3
|
||||||
while retry > 0:
|
while retry > 0:
|
||||||
try:
|
try:
|
||||||
@@ -43,7 +45,7 @@ def _init(client: LanguageServer, root: Path):
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def _open(client: LanguageServer, path: Path, text: Optional[str] = None):
|
def _open(client: LanguageServer, path: Path, text: Optional[str] = None) -> None:
|
||||||
if text is None:
|
if text is None:
|
||||||
with open(path) as fp:
|
with open(path) as fp:
|
||||||
text = fp.read()
|
text = fp.read()
|
||||||
@@ -55,8 +57,11 @@ def _open(client: LanguageServer, path: Path, text: Optional[str] = None):
|
|||||||
|
|
||||||
|
|
||||||
def _test_completion(
|
def _test_completion(
|
||||||
client_server, datadir, content: str, context: Optional[CompletionContext]
|
client_server: Tuple[LanguageServer, CMakeLanguageServer],
|
||||||
):
|
datadir: Path,
|
||||||
|
content: str,
|
||||||
|
context: Optional[CompletionContext],
|
||||||
|
) -> CompletionList:
|
||||||
client, server = client_server
|
client, server = client_server
|
||||||
_init(client, datadir)
|
_init(client, datadir)
|
||||||
path = datadir / "CMakeLists.txt"
|
path = datadir / "CMakeLists.txt"
|
||||||
@@ -70,7 +75,9 @@ def _test_completion(
|
|||||||
return client.lsp.send_request(COMPLETION, params).result(timeout=CALL_TIMEOUT)
|
return client.lsp.send_request(COMPLETION, params).result(timeout=CALL_TIMEOUT)
|
||||||
|
|
||||||
|
|
||||||
def test_initialize(client_server, datadir):
|
def test_initialize(
|
||||||
|
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||||
|
) -> None:
|
||||||
client, server = client_server
|
client, server = client_server
|
||||||
|
|
||||||
assert server._api is None
|
assert server._api is None
|
||||||
@@ -78,7 +85,9 @@ def test_initialize(client_server, datadir):
|
|||||||
assert server._api is not None
|
assert server._api is not None
|
||||||
|
|
||||||
|
|
||||||
def test_completions_invoked(client_server, datadir):
|
def test_completions_invoked(
|
||||||
|
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||||
|
) -> None:
|
||||||
response = _test_completion(
|
response = _test_completion(
|
||||||
client_server,
|
client_server,
|
||||||
datadir,
|
datadir,
|
||||||
@@ -90,14 +99,18 @@ def test_completions_invoked(client_server, datadir):
|
|||||||
assert "<PROJECT-NAME>" in item.documentation
|
assert "<PROJECT-NAME>" in item.documentation
|
||||||
|
|
||||||
|
|
||||||
def test_completions_nocontext(client_server, datadir):
|
def test_completions_nocontext(
|
||||||
|
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||||
|
) -> None:
|
||||||
response = _test_completion(client_server, datadir, "projec", None)
|
response = _test_completion(client_server, datadir, "projec", None)
|
||||||
item = next(filter(lambda x: x.label == "project", response.items), None)
|
item = next(filter(lambda x: x.label == "project", response.items), None)
|
||||||
assert item is not None
|
assert item is not None
|
||||||
assert "<PROJECT-NAME>" in item.documentation
|
assert "<PROJECT-NAME>" in item.documentation
|
||||||
|
|
||||||
|
|
||||||
def test_completions_triggercharacter_variable(client_server, datadir):
|
def test_completions_triggercharacter_variable(
|
||||||
|
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||||
|
) -> None:
|
||||||
response = _test_completion(
|
response = _test_completion(
|
||||||
client_server,
|
client_server,
|
||||||
datadir,
|
datadir,
|
||||||
@@ -110,7 +123,9 @@ def test_completions_triggercharacter_variable(client_server, datadir):
|
|||||||
assert response == response_nocontext
|
assert response == response_nocontext
|
||||||
|
|
||||||
|
|
||||||
def test_completions_triggercharacter_module(client_server, datadir):
|
def test_completions_triggercharacter_module(
|
||||||
|
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||||
|
) -> None:
|
||||||
response = _test_completion(
|
response = _test_completion(
|
||||||
client_server,
|
client_server,
|
||||||
datadir,
|
datadir,
|
||||||
@@ -123,7 +138,9 @@ def test_completions_triggercharacter_module(client_server, datadir):
|
|||||||
assert response == response_nocontext
|
assert response == response_nocontext
|
||||||
|
|
||||||
|
|
||||||
def test_completions_triggercharacter_package(client_server, datadir):
|
def test_completions_triggercharacter_package(
|
||||||
|
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||||
|
) -> None:
|
||||||
response = _test_completion(
|
response = _test_completion(
|
||||||
client_server,
|
client_server,
|
||||||
datadir,
|
datadir,
|
||||||
@@ -136,7 +153,9 @@ def test_completions_triggercharacter_package(client_server, datadir):
|
|||||||
assert response == response_nocontext
|
assert response == response_nocontext
|
||||||
|
|
||||||
|
|
||||||
def test_formatting(client_server, datadir):
|
def test_formatting(
|
||||||
|
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||||
|
) -> None:
|
||||||
client, server = client_server
|
client, server = client_server
|
||||||
_init(client, datadir)
|
_init(client, datadir)
|
||||||
path = datadir / "CMakeLists.txt"
|
path = datadir / "CMakeLists.txt"
|
||||||
@@ -150,7 +169,9 @@ def test_formatting(client_server, datadir):
|
|||||||
assert response[0].newText == "a(b c)\n"
|
assert response[0].newText == "a(b c)\n"
|
||||||
|
|
||||||
|
|
||||||
def test_hover(client_server, datadir):
|
def test_hover(
|
||||||
|
client_server: Tuple[LanguageServer, CMakeLanguageServer], datadir: Path
|
||||||
|
) -> None:
|
||||||
client, server = client_server
|
client, server = client_server
|
||||||
_init(client, datadir)
|
_init(client, datadir)
|
||||||
path = datadir / "CMakeLists.txt"
|
path = datadir / "CMakeLists.txt"
|
||||||
|
|||||||
27
tox.ini
27
tox.ini
@@ -1,25 +1,15 @@
|
|||||||
[isort]
|
|
||||||
multi_line_output = 3
|
|
||||||
include_trailing_comma = True
|
|
||||||
force_grid_wrap = 0
|
|
||||||
use_parentheses = True
|
|
||||||
ensure_newline_before_comments = True
|
|
||||||
line_length = 88
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
max-line-length = 88
|
|
||||||
extend-ignore = E203, W503
|
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
isolated_build = True
|
isolated_build = True
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
envlist = py36, py37, py38, lint
|
envlist = py36, py37, py38, py39, lint
|
||||||
|
allowlist_externals = git
|
||||||
|
|
||||||
[gh-actions]
|
[gh-actions]
|
||||||
python =
|
python =
|
||||||
3.6: py36
|
3.6: py36
|
||||||
3.7: py37, lint
|
3.7: py37
|
||||||
3.8: py38
|
3.8: py38, lint
|
||||||
|
3.9: py39
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
whitelist_externals = poetry
|
whitelist_externals = poetry
|
||||||
@@ -32,7 +22,6 @@ commands =
|
|||||||
|
|
||||||
[testenv:lint]
|
[testenv:lint]
|
||||||
commands =
|
commands =
|
||||||
poetry run isort -c -rc src tests
|
poetry run pysen run format
|
||||||
poetry run black --diff src tests
|
git diff --exit-code --ignore-submodules
|
||||||
poetry run flake8 src tests
|
poetry run pysen run lint
|
||||||
poetry run mypy src tests
|
|
||||||
|
|||||||
Reference in New Issue
Block a user