@@ -60,9 +60,10 @@ class API(object):
|
|||||||
}''')
|
}''')
|
||||||
|
|
||||||
proc = subprocess.run([self._cmake, str(self._build)],
|
proc = subprocess.run([self._cmake, str(self._build)],
|
||||||
universal_newlines=True,
|
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE,
|
||||||
|
encoding='utf-8',
|
||||||
|
universal_newlines=True)
|
||||||
self.query_json.unlink()
|
self.query_json.unlink()
|
||||||
self.query_json.parent.rmdir()
|
self.query_json.parent.rmdir()
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
@@ -144,10 +145,11 @@ endforeach()
|
|||||||
''')
|
''')
|
||||||
p = subprocess.run(
|
p = subprocess.run(
|
||||||
[self._cmake, '-P', str(tmplist)],
|
[self._cmake, '-P', str(tmplist)],
|
||||||
cwd=cmake_files['paths']['source'],
|
|
||||||
universal_newlines=True,
|
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE,
|
||||||
|
cwd=cmake_files['paths']['source'],
|
||||||
|
encoding='utf-8',
|
||||||
|
universal_newlines=True)
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -190,6 +192,7 @@ endforeach()
|
|||||||
def _parse_commands(self) -> None:
|
def _parse_commands(self) -> None:
|
||||||
p = subprocess.run([self._cmake, '--help-commands'],
|
p = subprocess.run([self._cmake, '--help-commands'],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
|
encoding='utf-8',
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
|
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
@@ -213,6 +216,7 @@ endforeach()
|
|||||||
def _parse_variables(self) -> None:
|
def _parse_variables(self) -> None:
|
||||||
p = subprocess.run([self._cmake, '--help-variables'],
|
p = subprocess.run([self._cmake, '--help-variables'],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
|
encoding='utf-8',
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
|
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
@@ -241,6 +245,7 @@ endforeach()
|
|||||||
def _parse_modules(self) -> None:
|
def _parse_modules(self) -> None:
|
||||||
p = subprocess.run([self._cmake, '--help-modules'],
|
p = subprocess.run([self._cmake, '--help-modules'],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
|
encoding='utf-8',
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
|
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
|
|||||||
@@ -40,12 +40,21 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
self._api = API(cmake, Path(builddir))
|
self._api = API(cmake, Path(builddir))
|
||||||
self._api.parse_doc()
|
self._api.parse_doc()
|
||||||
|
|
||||||
@self.feature(COMPLETION, trigger_characters=['{', '('])
|
trigger_characters = ['{', '(']
|
||||||
|
|
||||||
|
@self.feature(COMPLETION, trigger_characters=trigger_characters)
|
||||||
def completions(params: CompletionParams):
|
def completions(params: CompletionParams):
|
||||||
if (hasattr(params, 'context') and params.context.triggerKind ==
|
if (hasattr(params, 'context') and params.context.triggerKind ==
|
||||||
CompletionTriggerKind.TriggerCharacter):
|
CompletionTriggerKind.TriggerCharacter):
|
||||||
token = ''
|
token = ''
|
||||||
trigger = params.context.triggerCharacter
|
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:
|
else:
|
||||||
word = self._cursor_word(params.textDocument.uri,
|
word = self._cursor_word(params.textDocument.uri,
|
||||||
params.position, False)
|
params.position, False)
|
||||||
@@ -59,21 +68,21 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
items.extend(
|
items.extend(
|
||||||
CompletionItem(x,
|
CompletionItem(x,
|
||||||
CompletionItemKind.Function,
|
CompletionItemKind.Function,
|
||||||
documentation=self._api.get_command_doc(x))
|
documentation=self._api.get_command_doc(x),
|
||||||
for x in commands)
|
insert_text=x) for x in commands)
|
||||||
|
|
||||||
if trigger is None or trigger == '{':
|
if trigger is None or trigger == '{':
|
||||||
variables = self._api.search_variable(token)
|
variables = self._api.search_variable(token)
|
||||||
items.extend(
|
items.extend(
|
||||||
CompletionItem(x,
|
CompletionItem(x,
|
||||||
CompletionItemKind.Variable,
|
CompletionItemKind.Variable,
|
||||||
documentation=self._api.get_variable_doc(x))
|
documentation=self._api.get_variable_doc(x),
|
||||||
for x in variables)
|
insert_text=x) for x in variables)
|
||||||
|
|
||||||
if trigger is None:
|
if trigger is None:
|
||||||
targets = self._api.search_target(token)
|
targets = self._api.search_target(token)
|
||||||
items.extend(
|
items.extend(
|
||||||
CompletionItem(x, CompletionItemKind.Class)
|
CompletionItem(x, CompletionItemKind.Class, insert_text=x)
|
||||||
for x in targets)
|
for x in targets)
|
||||||
|
|
||||||
if trigger == '(':
|
if trigger == '(':
|
||||||
@@ -87,16 +96,16 @@ class CMakeLanguageServer(LanguageServer):
|
|||||||
CompletionItem(x,
|
CompletionItem(x,
|
||||||
CompletionItemKind.Module,
|
CompletionItemKind.Module,
|
||||||
documentation=self._api.
|
documentation=self._api.
|
||||||
get_module_doc(x, False))
|
get_module_doc(x, False),
|
||||||
for x in modules)
|
insert_text=x) for x in modules)
|
||||||
elif func == 'find_package':
|
elif func == 'find_package':
|
||||||
modules = self._api.search_module(token, True)
|
modules = self._api.search_module(token, True)
|
||||||
items.extend(
|
items.extend(
|
||||||
CompletionItem(x,
|
CompletionItem(x,
|
||||||
CompletionItemKind.Module,
|
CompletionItemKind.Module,
|
||||||
documentation=self._api.
|
documentation=self._api.
|
||||||
get_module_doc(x, True))
|
get_module_doc(x, True),
|
||||||
for x in modules)
|
insert_text=x) for x in modules)
|
||||||
|
|
||||||
return CompletionList(False, items)
|
return CompletionList(False, items)
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,21 @@ def _open(client: LanguageServer, path: Path, text: Optional[str] = None):
|
|||||||
TextDocumentItem(path.as_uri(), 'cmake', 1, text)))
|
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):
|
def test_initialize(client_server, datadir):
|
||||||
client, server = client_server
|
client, server = client_server
|
||||||
|
|
||||||
@@ -49,78 +64,52 @@ def test_initialize(client_server, datadir):
|
|||||||
|
|
||||||
|
|
||||||
def test_completions_invoked(client_server, datadir):
|
def test_completions_invoked(client_server, datadir):
|
||||||
client, server = client_server
|
response = _test_completion(
|
||||||
_init(client, datadir)
|
client_server, datadir, 'projec',
|
||||||
path = datadir / 'CMakeLists.txt'
|
CompletionContext(CompletionTriggerKind.Invoked))
|
||||||
_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)
|
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_no_context(client_server, datadir):
|
def test_completions_nocontext(client_server, datadir):
|
||||||
client, server = client_server
|
response = _test_completion(client_server, datadir, 'projec', None)
|
||||||
_init(client, datadir)
|
|
||||||
path = datadir / 'CMakeLists.txt'
|
|
||||||
_open(client, path, 'projec')
|
|
||||||
params = CompletionParams(TextDocumentIdentifier(path.as_uri()),
|
|
||||||
Position(0, 6),
|
|
||||||
CompletionContext(CompletionTriggerKind.Invoked))
|
|
||||||
# some clients do not send context
|
|
||||||
del params.context
|
|
||||||
response = client.lsp.send_request(COMPLETION,
|
|
||||||
params).result(timeout=CALL_TIMEOUT)
|
|
||||||
item = next(filter(lambda x: x.label == 'project', response.items), None)
|
item = next(filter(lambda x: x.label == 'project', response.items), None)
|
||||||
assert item is not None
|
assert item is not None
|
||||||
assert '<PROJECT-NAME>' in item.documentation
|
assert '<PROJECT-NAME>' in item.documentation
|
||||||
|
|
||||||
|
|
||||||
def test_completions_triggercharacter_variable(client_server, datadir):
|
def test_completions_triggercharacter_variable(client_server, datadir):
|
||||||
client, server = client_server
|
response = _test_completion(
|
||||||
_init(client, datadir)
|
client_server, datadir, '${',
|
||||||
path = datadir / 'CMakeLists.txt'
|
CompletionContext(CompletionTriggerKind.TriggerCharacter, '{'))
|
||||||
_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]
|
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):
|
def test_completions_triggercharacter_module(client_server, datadir):
|
||||||
client, server = client_server
|
response = _test_completion(
|
||||||
_init(client, datadir)
|
client_server, datadir, 'include(',
|
||||||
path = datadir / 'CMakeLists.txt'
|
CompletionContext(CompletionTriggerKind.TriggerCharacter, '('))
|
||||||
_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]
|
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):
|
def test_completions_triggercharacter_package(client_server, datadir):
|
||||||
client, server = client_server
|
response = _test_completion(
|
||||||
_init(client, datadir)
|
client_server, datadir, 'find_package(',
|
||||||
path = datadir / 'CMakeLists.txt'
|
CompletionContext(CompletionTriggerKind.TriggerCharacter, '('))
|
||||||
_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]
|
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):
|
def test_formatting(client_server, datadir):
|
||||||
client, server = client_server
|
client, server = client_server
|
||||||
|
|||||||
Reference in New Issue
Block a user