From cb0ccc155c124923485eed7b0b8b4cc439be09c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=A8=E5=8D=93?= Date: Tue, 11 Oct 2022 13:49:33 +0800 Subject: [PATCH] built in commands variables auto complete --- build/yaml-to-json.mjs | 2 +- package.json | 10 ++--- server/src/builtin-cmds.json | 51 ++++++++++++++-------- server/src/server.ts | 82 +++++++++++++++++++++++++++++------- 4 files changed, 106 insertions(+), 39 deletions(-) diff --git a/build/yaml-to-json.mjs b/build/yaml-to-json.mjs index ef81772..7f11160 100644 --- a/build/yaml-to-json.mjs +++ b/build/yaml-to-json.mjs @@ -13,7 +13,7 @@ function isGrammarOutdate(fileName) { const yaml = json.replace('.json', '.yml'); const jsonState = statSync(json); const yamlState = statSync(yaml); - if (yamlState.mtime.toString() > jsonState.mtime.toString()) { + if (yamlState.mtimeMs > jsonState.mtimeMs) { console.log(`${yaml} changed, Grammar is outdate, re-generate it.`); return true; } diff --git a/package.json b/package.json index 500626a..78ae6b1 100644 --- a/package.json +++ b/package.json @@ -107,16 +107,16 @@ }, "scripts": { "vscode:prepublish": "npm run compile", - "grammar-cmake": "npx js-yaml ./syntaxes/cmake.tmLanguage.yml > ./syntaxes/cmake.tmLanguage.json", - "grammar-cmakecache": "npx js-yaml ./syntaxes/cmakecache.tmLanguage.yml > ./syntaxes/cmakecache.tmLanguage.json", - "grammar-cmdsignature": "npx js-yaml ./syntaxes/cmdsignature.tmLanguage.yml > ./syntaxes/cmdsignature.tmLanguage.json", - "grammar": "node ./build/yaml-to-json.mjs", "compile": "npm run grammar && tsc -b . --verbose", "watch": "npm run grammar && tsc -watch -b . --verbose", "pretest": "npm run compile && npm run lint", "lint": "eslint src --ext ts", "test": "node ./out/test/runTest.js", - "postinstall": "cd client && npm install && cd ../server && npm install && cd .." + "postinstall": "cd client && npm install && cd ../server && npm install && cd ..", + "grammar-cmake": "npx js-yaml ./syntaxes/cmake.tmLanguage.yml > ./syntaxes/cmake.tmLanguage.json", + "grammar-cmakecache": "npx js-yaml ./syntaxes/cmakecache.tmLanguage.yml > ./syntaxes/cmakecache.tmLanguage.json", + "grammar-cmdsignature": "npx js-yaml ./syntaxes/cmdsignature.tmLanguage.yml > ./syntaxes/cmdsignature.tmLanguage.json", + "grammar": "node ./build/yaml-to-json.mjs" }, "devDependencies": { "@types/mocha": "^9.1.1", diff --git a/server/src/builtin-cmds.json b/server/src/builtin-cmds.json index e2cadbf..1a0cc7e 100644 --- a/server/src/builtin-cmds.json +++ b/server/src/builtin-cmds.json @@ -98,7 +98,8 @@ "doc": "Disallowed since version 3.0. Sets the specified variable to a string representing the platform and compiler settings", "sig": [ "build_name(variable)" - ] + ], + "deprecated": true }, "cmake_host_system_information": { "doc": "Query various host system information.", @@ -371,7 +372,8 @@ "doc": "Run an executable program during the processing of the CMakeList.txt file.", "sig": [ "exec_program(Executable [directory in which to run] [ARGS ] [OUTPUT_VARIABLE ] [RETURN_VALUE ])" - ] + ], + "deprecated": true }, "execute_process": { "doc": "Execute one or more child processes.", @@ -392,7 +394,8 @@ "doc": "Disallowed since version 3.0. Create a file named that can be included into a CMake listfile with the INCLUDE command.", "sig": [ "export_library_dependencies( [APPEND])" - ] + ], + "deprecated": true }, "file": { "doc": "File manipulation command.", @@ -644,7 +647,8 @@ "install_files( extension file file ...)", "install_files( regexp)", "install_files( FILES file file ...)" - ] + ], + "deprecated": true }, "install_programs": { "doc": "Deprecated since version 3.0: Use the install(PROGRAMS) command instead.", @@ -652,7 +656,8 @@ "install_programs( file1 file2 [file3 ...])", "install_programs( FILES file1 [file2 ...])", "install_programs( regexp)" - ] + ], + "deprecated": true }, "install_targets": { "doc": "Deprecated since version 3.0: Use the install(TARGETS) command instead.", @@ -705,7 +710,8 @@ "doc": "Disallowed since version 3.0. See CMake Policy CMP0031. Load a command into a running CMake.", "sig": [ "load_command(COMMAND_NAME [loc2 ...])" - ] + ], + "deprecated": true }, "macro": { "doc": "Start recording a macro for later invocation as a command.", @@ -717,7 +723,8 @@ "doc": "Deprecated since version 3.0: Use the file(MAKE_DIRECTORY) command instead.", "sig": [ "make_directory(directory)" - ] + ], + "deprecated": true }, "mark_as_advanced": { "doc": "Mark cmake cached variables as advanced.", @@ -748,7 +755,8 @@ "doc": "Disallowed since version 3.0. See CMake Policy CMP0032.", "sig": [ "output_required_files(srcfile outputfile)" - ] + ], + "deprecated": true }, "project": { "doc": "Set the name of the project.", @@ -762,19 +770,22 @@ "doc": "Deprecated since version 3.14: This command was originally added to support Qt 3 before the add_custom_command() command was sufficiently mature.", "sig": [ "qt_wrap_cpp(resultingLibraryName DestName SourceLists ...)" - ] + ], + "deprecated": true }, "qt_wrap_ui": { "doc": "Deprecated since version 3.14: This command was originally added to support Qt 3 before the add_custom_command() command was sufficiently mature.", "sig": [ "qt_wrap_ui(resultingLibraryName HeadersDestName SourcesDestName SourceLists ...)" - ] + ], + "deprecated": true }, "remove": { "doc": "Deprecated since version 3.0: Use the list(REMOVE_ITEM) command instead.", "sig": [ "remove(VAR VALUE VALUE ...)" - ] + ], + "deprecated": true }, "remove_definitions": { "doc": "Remove -D define flags added by add_definitions().", @@ -895,13 +906,15 @@ "doc": "Disallowed since version 3.0. See CMake Policy CMP0029.", "sig": [ "subdir_depends(subdir dep1 dep2 ...)" - ] + ], + "deprecated": true }, "subdirs": { "doc": "Deprecated since version 3.0: Use the add_subdirectory() command instead.", "sig": [ "subdirs(dir1 dir2 ...[EXCLUDE_FROM_ALL exclude_dir1 exclude_dir2 ...] [PREORDER])" - ] + ], + "deprecated": true }, "target_compile_definitions": { "doc": "Add compile definitions to a target.", @@ -989,19 +1002,22 @@ "doc": "Disallowed since version 3.0. See CMake Policy CMP0030.", "sig": [ "use_mangled_mesa(PATH_TO_MESA OUTPUT_DIRECTORY)" - ] + ], + "deprecated": true }, "utility_source": { "doc": "Disallowed since version 3.0. See CMake Policy CMP0034.", "sig": [ "utility_source(cache_entry executable_name path_to_source [file1 file2 ...])" - ] + ], + "deprecated": true }, "variable_requires": { "doc": "Disallowed since version 3.0. See CMake Policy CMP0035.", "sig": [ "variable_requires(TEST_VARIABLE RESULT_VARIABLE REQUIRED_VARIABLE1 REQUIRED_VARIABLE2 ...)" - ] + ], + "deprecated": true }, "variable_watch": { "doc": "Watch the CMake variable for change.", @@ -1044,6 +1060,7 @@ "doc": "Deprecated since version 3.0: Use the file(WRITE) command instead.", "sig": [ "write_file(filename \"message to write\"... [APPEND])" - ] + ], + "deprecated": true } } \ No newline at end of file diff --git a/server/src/server.ts b/server/src/server.ts index aadea53..77cc735 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,21 +1,15 @@ import { - createConnection, - TextDocuments, - ProposedFeatures, - InitializeParams, - InitializeResult, - TextDocumentSyncKind, - HoverParams, - SignatureHelpParams - + createConnection, HoverParams, InitializeParams, InitializeResult, + ProposedFeatures, SignatureHelpParams, TextDocuments, TextDocumentSyncKind } from 'vscode-languageserver/node'; -import { TextDocument, Range } from 'vscode-languageserver-textdocument'; -import { Position } from 'vscode-languageserver-types'; +import { CompletionItemKind, CompletionParams } from 'vscode-languageserver-protocol'; +import { Range, TextDocument } from 'vscode-languageserver-textdocument'; +import { CompletionItem, CompletionItemTag, Position } from 'vscode-languageserver-types'; -import * as builtinCmds from './builtin-cmds.json'; -import { Entries, getBuiltinEntries } from './utils'; import { exec } from 'child_process'; +import { Entries, getBuiltinEntries } from './utils'; +import * as builtinCmds from './builtin-cmds.json'; const entries: Entries = getBuiltinEntries(); const modules = entries[0].split('\n'); @@ -38,7 +32,8 @@ connection.onInitialize((params: InitializeParams) => { signatureHelpProvider: { triggerCharacters: ['('], retriggerCharacters: [' '] - } + }, + completionProvider: {} }, serverInfo: { name: 'cmakels', @@ -57,7 +52,7 @@ connection.onHover((params: HoverParams) => { const document: TextDocument = documents.get(params.textDocument.uri); const word = getWordAtPosition(document, params.position); if (word.length === 0) { - return undefined; + return null; } // check if the word is a builtin commands @@ -110,6 +105,23 @@ connection.onHover((params: HoverParams) => { } }); +connection.onCompletion(async (params: CompletionParams) => { + const document = documents.get(params.textDocument.uri); + const word = getWordAtPosition(document, params.position); + if (word.length === 0) { + return null; + } + + const results = await Promise.all([ + getCommandProposals(word), + getProposals(word, CompletionItemKind.Module, modules), + getProposals(word, CompletionItemKind.Constant, policies), + getProposals(word, CompletionItemKind.Variable, variables), + getProposals(word, CompletionItemKind.Property, properties) + ]); + return results.flat(); +}); + connection.onSignatureHelp((params: SignatureHelpParams) => { return null; }); @@ -131,12 +143,50 @@ function getWordAtPosition(textDocument: TextDocument, position: Position): stri // TODO: the regex expression capture numbers, fix it. const startReg = /[a-zA-Z0-9_]*$/, endReg = /^[a-zA-Z0-9_]*/; - + const startWord = start.match(startReg)[0], endWord = end.match(endReg)[0]; return startWord + endWord; } +function getCommandProposals(word: string): Thenable { + return new Promise((resolve, rejects) => { + const similarCmds = Object.keys(builtinCmds).filter(cmd => { + return cmd.includes(word); + }); + const proposalCmds: CompletionItem[] = similarCmds.map((value, index, array) => { + let item: CompletionItem = { + label: value, + kind: CompletionItemKind.Function, + }; + + if ("deprecated" in builtinCmds[value]) { + item.tags = [CompletionItemTag.Deprecated]; + } + return item; + }); + + resolve(proposalCmds); + }); +} + +function getProposals(word: string, kind: CompletionItemKind, dataSource: string[]): Thenable { + return new Promise((resolve, rejects) => { + const similar = dataSource.filter(candidate => { + return candidate.includes(word); + }); + + const proposals: CompletionItem[] = similar.map((value, index, array) => { + return { + label: value, + kind: kind + }; + }); + + resolve(proposals); + }); +} + // Make the text document manager listen on the connection // for open, change and close text document events documents.listen(connection);