Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d916b6989 | ||
|
|
1c606ee8a8 | ||
|
|
cdb62adce3 | ||
|
|
6ac3b1d17f | ||
|
|
ce2c3a21db | ||
|
|
3697fae2d3 | ||
|
|
48d5980a36 | ||
|
|
e07b3242c8 | ||
|
|
2d36887b26 | ||
|
|
67aced6544 | ||
|
|
3c171b9e25 | ||
|
|
c8c284e061 | ||
|
|
6bf08e0f14 | ||
|
|
40d93525d9 | ||
|
|
f8136d6dbc | ||
|
|
5af6555d3c |
7
.github/workflows/tests.yml
vendored
7
.github/workflows/tests.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
python: [3.6, 3.7, 3.8]
|
||||
os: [ubuntu-18.04, windows-2016]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
@@ -21,12 +21,13 @@ jobs:
|
||||
- name: Install CMake
|
||||
if: contains(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
CMAKE_VERSION=3.14.7
|
||||
CMAKE_VERSION=3.17.3
|
||||
curl -sSL https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz | tar xz
|
||||
sudo cp -rT cmake-$CMAKE_VERSION-Linux-x86_64 /usr/local
|
||||
rm -rf cmake-$CMAKE_VERSION-Linux-x86_64
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cmake --version
|
||||
python -m pip install --upgrade setuptools pip wheel
|
||||
python -m pip install poetry tox-gh-actions
|
||||
- name: Test with tox
|
||||
@@ -38,4 +39,4 @@ jobs:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: unittests
|
||||
name: Python ${{ matrix.python }} on ${{ matrix.os }}
|
||||
fail_ci_if_error: true
|
||||
fail_ci_if_error: false
|
||||
|
||||
22
README.md
22
README.md
@@ -25,12 +25,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 +48,21 @@ $ 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
|
||||
```
|
||||
|
||||
[coc.nvim]: https://github.com/neoclide/coc.nvim
|
||||
[vim-lsp]: https://github.com/prabirshrestha/vim-lsp
|
||||
|
||||
@@ -4,5 +4,4 @@ coverage:
|
||||
default:
|
||||
threshold: 10%
|
||||
patch:
|
||||
default:
|
||||
threshold: 20%
|
||||
default: off
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "cmake-language-server"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
description = "CMake LSP Implementation"
|
||||
license = "MIT"
|
||||
authors = ["regen"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '0.1.1'
|
||||
__version__ = '0.1.2'
|
||||
|
||||
@@ -60,9 +60,10 @@ class API(object):
|
||||
}''')
|
||||
|
||||
proc = subprocess.run([self._cmake, str(self._build)],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stderr=subprocess.PIPE,
|
||||
encoding='utf-8',
|
||||
universal_newlines=True)
|
||||
self.query_json.unlink()
|
||||
self.query_json.parent.rmdir()
|
||||
if proc.returncode != 0:
|
||||
@@ -144,10 +145,11 @@ endforeach()
|
||||
''')
|
||||
p = subprocess.run(
|
||||
[self._cmake, '-P', str(tmplist)],
|
||||
cwd=cmake_files['paths']['source'],
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cmake_files['paths']['source'],
|
||||
encoding='utf-8',
|
||||
universal_newlines=True)
|
||||
if p.returncode != 0:
|
||||
return
|
||||
|
||||
@@ -190,6 +192,7 @@ endforeach()
|
||||
def _parse_commands(self) -> None:
|
||||
p = subprocess.run([self._cmake, '--help-commands'],
|
||||
stdout=subprocess.PIPE,
|
||||
encoding='utf-8',
|
||||
universal_newlines=True)
|
||||
|
||||
if p.returncode != 0:
|
||||
@@ -213,6 +216,7 @@ endforeach()
|
||||
def _parse_variables(self) -> None:
|
||||
p = subprocess.run([self._cmake, '--help-variables'],
|
||||
stdout=subprocess.PIPE,
|
||||
encoding='utf-8',
|
||||
universal_newlines=True)
|
||||
|
||||
if p.returncode != 0:
|
||||
@@ -241,6 +245,7 @@ endforeach()
|
||||
def _parse_modules(self) -> None:
|
||||
p = subprocess.run([self._cmake, '--help-modules'],
|
||||
stdout=subprocess.PIPE,
|
||||
encoding='utf-8',
|
||||
universal_newlines=True)
|
||||
|
||||
if p.returncode != 0:
|
||||
|
||||
@@ -40,12 +40,21 @@ class CMakeLanguageServer(LanguageServer):
|
||||
self._api = API(cmake, Path(builddir))
|
||||
self._api.parse_doc()
|
||||
|
||||
@self.feature(COMPLETION, trigger_characters=['{', '('])
|
||||
trigger_characters = ['{', '(']
|
||||
|
||||
@self.feature(COMPLETION, trigger_characters=trigger_characters)
|
||||
def completions(params: CompletionParams):
|
||||
if (params.context.triggerKind ==
|
||||
if (hasattr(params, 'context') and params.context.triggerKind ==
|
||||
CompletionTriggerKind.TriggerCharacter):
|
||||
token = ''
|
||||
trigger = params.context.triggerCharacter
|
||||
else:
|
||||
line = self._cursor_line(params.textDocument.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.textDocument.uri,
|
||||
params.position, False)
|
||||
@@ -59,21 +68,21 @@ class CMakeLanguageServer(LanguageServer):
|
||||
items.extend(
|
||||
CompletionItem(x,
|
||||
CompletionItemKind.Function,
|
||||
documentation=self._api.get_command_doc(x))
|
||||
for x in commands)
|
||||
documentation=self._api.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(x,
|
||||
CompletionItemKind.Variable,
|
||||
documentation=self._api.get_variable_doc(x))
|
||||
for x in variables)
|
||||
documentation=self._api.get_variable_doc(x),
|
||||
insert_text=x) for x in variables)
|
||||
|
||||
if trigger is None:
|
||||
targets = self._api.search_target(token)
|
||||
items.extend(
|
||||
CompletionItem(x, CompletionItemKind.Class)
|
||||
CompletionItem(x, CompletionItemKind.Class, insert_text=x)
|
||||
for x in targets)
|
||||
|
||||
if trigger == '(':
|
||||
@@ -87,16 +96,16 @@ class CMakeLanguageServer(LanguageServer):
|
||||
CompletionItem(x,
|
||||
CompletionItemKind.Module,
|
||||
documentation=self._api.
|
||||
get_module_doc(x, False))
|
||||
for x in modules)
|
||||
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(x,
|
||||
CompletionItemKind.Module,
|
||||
documentation=self._api.
|
||||
get_module_doc(x, True))
|
||||
for x in modules)
|
||||
get_module_doc(x, True),
|
||||
insert_text=x) for x in modules)
|
||||
|
||||
return CompletionList(False, items)
|
||||
|
||||
@@ -172,7 +181,16 @@ class CMakeLanguageServer(LanguageServer):
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
def main(args=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__}')
|
||||
args = parser.parse_args(args)
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logging.getLogger('pygls').setLevel(logging.WARNING)
|
||||
CMakeLanguageServer().start_io()
|
||||
|
||||
@@ -40,6 +40,21 @@ def _open(client: LanguageServer, path: Path, text: Optional[str] = None):
|
||||
TextDocumentItem(path.as_uri(), 'cmake', 1, text)))
|
||||
|
||||
|
||||
def _test_completion(client_server, datadir, content: str,
|
||||
context: Optional[CompletionContext]):
|
||||
client, server = client_server
|
||||
_init(client, datadir)
|
||||
path = datadir / 'CMakeLists.txt'
|
||||
_open(client, path, content)
|
||||
params = CompletionParams(TextDocumentIdentifier(path.as_uri()),
|
||||
Position(0, len(content)), context)
|
||||
if context is None:
|
||||
# some clients do not send context
|
||||
del params.context
|
||||
return client.lsp.send_request(COMPLETION,
|
||||
params).result(timeout=CALL_TIMEOUT)
|
||||
|
||||
|
||||
def test_initialize(client_server, datadir):
|
||||
client, server = client_server
|
||||
|
||||
@@ -49,61 +64,52 @@ def test_initialize(client_server, datadir):
|
||||
|
||||
|
||||
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)
|
||||
response = _test_completion(
|
||||
client_server, datadir, 'projec',
|
||||
CompletionContext(CompletionTriggerKind.Invoked))
|
||||
item = next(filter(lambda x: x.label == 'project', response.items), None)
|
||||
assert item is not None
|
||||
assert '<PROJECT-NAME>' in item.documentation
|
||||
|
||||
|
||||
def test_completions_nocontext(client_server, datadir):
|
||||
response = _test_completion(client_server, datadir, 'projec', None)
|
||||
item = next(filter(lambda x: x.label == 'project', response.items), None)
|
||||
assert item is not None
|
||||
assert '<PROJECT-NAME>' in item.documentation
|
||||
|
||||
|
||||
def test_completions_triggercharacter_variable(client_server, datadir):
|
||||
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)
|
||||
response = _test_completion(
|
||||
client_server, datadir, '${',
|
||||
CompletionContext(CompletionTriggerKind.TriggerCharacter, '{'))
|
||||
assert 'PROJECT_VERSION' in [x.label for x in response.items]
|
||||
|
||||
response_nocontext = _test_completion(client_server, datadir, '${', None)
|
||||
assert response == response_nocontext
|
||||
|
||||
|
||||
def test_completions_triggercharacter_module(client_server, datadir):
|
||||
client, server = client_server
|
||||
_init(client, datadir)
|
||||
path = datadir / 'CMakeLists.txt'
|
||||
_open(client, path, 'include(')
|
||||
response = client.lsp.send_request(
|
||||
COMPLETION,
|
||||
CompletionParams(
|
||||
TextDocumentIdentifier(path.as_uri()), Position(0, 8),
|
||||
CompletionContext(CompletionTriggerKind.TriggerCharacter,
|
||||
'('))).result(timeout=CALL_TIMEOUT)
|
||||
response = _test_completion(
|
||||
client_server, datadir, 'include(',
|
||||
CompletionContext(CompletionTriggerKind.TriggerCharacter, '('))
|
||||
assert 'GoogleTest' in [x.label for x in response.items]
|
||||
|
||||
response_nocontext = _test_completion(client_server, datadir, 'include(',
|
||||
None)
|
||||
assert response == response_nocontext
|
||||
|
||||
|
||||
def test_completions_triggercharacter_package(client_server, 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)
|
||||
response = _test_completion(
|
||||
client_server, datadir, 'find_package(',
|
||||
CompletionContext(CompletionTriggerKind.TriggerCharacter, '('))
|
||||
assert 'Boost' in [x.label for x in response.items]
|
||||
|
||||
response_nocontext = _test_completion(client_server, datadir,
|
||||
'find_package(', None)
|
||||
assert response == response_nocontext
|
||||
|
||||
|
||||
def test_formatting(client_server, datadir):
|
||||
client, server = client_server
|
||||
|
||||
Reference in New Issue
Block a user