diff --git a/.gitignore b/.gitignore index 0b60dfa..5241ca1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist node_modules .vscode-test/ *.vsix +syntaxes/*.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 3b17e53..be52d11 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,18 +3,28 @@ { "version": "2.0.0", "tasks": [ - { - "type": "npm", - "script": "watch", - "problemMatcher": "$tsc-watch", - "isBackground": true, - "presentation": { - "reveal": "never" - }, - "group": { - "kind": "build", - "isDefault": true - } - } - ] + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": "build", + "label": "npm: watch", + "detail": "npm run grammar && tsc -watch -p ./" + }, + { + "type": "npm", + "script": "compile", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [], + "label": "npm: compile", + "detail": "npm run grammar && tsc -p ./" + } + ] } diff --git a/language-configuration.json b/language-configuration.json new file mode 100644 index 0000000..01c9f47 --- /dev/null +++ b/language-configuration.json @@ -0,0 +1,74 @@ +{ + "comments": { + "blockComment": [ + "#[=[", + "]=]" + ], + "lineComment": "#" + }, + "brackets": [ + [ + "(", + ")" + ], + [ + "[", + "]" + ], + [ + "{", + "}" + ] + ], + "autoClosingPairs": [ + { + "open": "{", + "close": "}" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + } + ], + "autoCloseBefore": ";:.,=}])>` \n\t", + "surroundingPairs": [ + [ + "(", + ")" + ], + [ + "[", + "]" + ], + [ + "{", + "}" + ], + [ + "\"", + "\"" + ] + ], + "folding": { + "markers": { + "start": "^\\s*#region\\b", + "end": "^\\s*#endregion\\b" + } + }, + "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)", + "indentationRules": { + "increaseIndentPattern": "^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$", + "decreaseIndentPattern": "^((?!.*?\\/\\*).*\\*/)?\\s*[\\)\\}\\]].*$" + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8f18ffa..0feaacb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,11 +17,12 @@ "@vscode/test-electron": "^2.1.5", "eslint": "^8.20.0", "glob": "^8.0.3", + "js-yaml": "^4.1.0", "mocha": "^10.0.0", "typescript": "^4.7.4" }, "engines": { - "vscode": "^1.70.0" + "vscode": "^1.68.0" } }, "node_modules/@eslint/eslintrc": { diff --git a/package.json b/package.json index 0a5cc63..064242f 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,14 @@ "vscode": "^1.68.0" }, "categories": [ - "Other" + "Programming Languages", + "Snippets", + "Formatters" ], "activationEvents": [ - "onCommand:cmake-ls.helloWorld" + "onCommand:cmake-ls.helloWorld", + "onLanguage:cmake", + "workspaceContains:CMakeLists.txt" ], "main": "./out/extension.js", "contributes": { @@ -19,27 +23,67 @@ "command": "cmake-ls.helloWorld", "title": "Hello World" } + ], + "grammars": [ + { + "language": "cmake", + "scopeName": "source.cmake", + "path": "./syntaxes/cmake.tmLanguage.json" + }, + { + "language": "cmakecache", + "scopeName": "source.cmakecache", + "path": "./syntaxes/cmakecache.tmLanguage.json" + } + ], + "languages": [ + { + "id": "cmake", + "extensions": [ + ".cmake" + ], + "filenames": [ + "CMakeLists.txt" + ], + "aliases": [ + "CMake" + ], + "configuration": "./language-configuration.json" + }, + { + "id": "cmakecache", + "filenames": [ + "CMakeCache.txt" + ], + "aliases": [ + "CMakeCache" + ] + } ] }, "scripts": { "vscode:prepublish": "npm run compile", - "compile": "tsc -p ./", - "watch": "tsc -watch -p ./", + "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": "npm run grammar-cmake && npm run grammar-cmakecache", + "compile": "npm run grammar && tsc -p ./", + "watch": "npm run grammar && tsc -watch -p ./", "pretest": "npm run compile && npm run lint", "lint": "eslint src --ext ts", "test": "node ./out/test/runTest.js" }, "devDependencies": { - "@types/vscode": "^1.70.0", "@types/glob": "^7.2.0", "@types/mocha": "^9.1.1", "@types/node": "16.x", + "@types/vscode": "^1.68.0", "@typescript-eslint/eslint-plugin": "^5.31.0", "@typescript-eslint/parser": "^5.31.0", + "@vscode/test-electron": "^2.1.5", "eslint": "^8.20.0", "glob": "^8.0.3", + "js-yaml": "^4.1.0", "mocha": "^10.0.0", - "typescript": "^4.7.4", - "@vscode/test-electron": "^2.1.5" + "typescript": "^4.7.4" } } diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt new file mode 100644 index 0000000..8eb4fd7 --- /dev/null +++ b/src/test/CMakeLists.txt @@ -0,0 +1,101 @@ +# This file is used to test the cmake-ls extension +cmake_minimum_required(VERSION 3.0) +project(Demo + VERSION 1.0 + LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) + +#[=[ +This is bracket block comment +The second line in comment +]=] + +# test bracket parameter +set(BRAKET_PARA [=[ +First line in bracket parameter +Second line in bracket parameter +]=]) + +# numbers +set(DIGIT 100) +set(DIGIT 100.1) + +# boolean constant +set(BOOL_TRUE YES) +set(BOOL_TRUE TRUE) +set(BOOL_FALSE NO) +set(BOOL_FALSE NOTFOUND) +set(BOOL_FALSE A-NOTFOUND) + +# deprecated command +exec_program(dir) +install_files(dest so libc.so) + +# flow control commands +if(TRUE) + message(STATUS "This message will print") +endif() + +set(INDEX 1) +while(TRUE) + message(STATUS ${INDEX}) + math(EXPR INDEX "${INDEX} + 1") + if (INDEX EQUAL 11) + break() + endif() +endwhile() + +foreach(INDEX RANGE 10) + message(STATUS ${INDEX}) +endforeach() + +# operator +if(DEFINED undefined_function) +endif() + +if ("gaga" STRLESS_EQUAL "haha") +endif() + +if (TRUE AND FALSE) +endif() + +# usr defined functions +function(print) + foreach(ARG IN LISTS ARGV) + endforeach() +endfunction() + +macro(macro_print) +endmacro() + +# test for cache variable +set(STR_VAR "demo string" CACHE STRING "This is docstring" FORCE) +set(BOOL_VAR "demo bool" CACHE BOOL "This is docstring" FORCE) +set(FILEPATH_VAR "demo filepath" CACHE FILEPATH "This is docstring" FORCE) +set(PATH_VAR "demo path" CACHE PATH "This is docstring" FORCE) +set(INTERNAL_VAR "demo internal" CACHE INTERNAL "This is docstring" FORCE) + +# variable reference +message(STATUS $ENV{PATH}) +message(STATUS $CACHE{CMAKE_BUILD_TYPE}) +message(STATUS "$ENV{PATH} plain strings") +message(STATUS "$CACHE{CMAKE_BUILD_TYPE}") +message(STATUS "${PATH}") +message(STATUS "${CMAKE_BUILD_TYPE}") + +# generator expression +# The $ check prevents adding anything if the property is empty, +# assuming the property value cannot be one of CMake's false constants. +set(prop $) +set(prop "$") +add_custom_target(run_some_tool + COMMAND some_tool "$<$:-I$>" + COMMAND_EXPAND_LISTS + VERBATIM +) + +# string escape +set(VAR "This a \"in string") \ No newline at end of file diff --git a/syntaxes/cmake.tmLanguage.yml b/syntaxes/cmake.tmLanguage.yml new file mode 100644 index 0000000..cdfeee7 --- /dev/null +++ b/syntaxes/cmake.tmLanguage.yml @@ -0,0 +1,85 @@ +fileTypes: + - cmake + - CMakeLists.txt +scopeName: source.cmake +# a dictionary (i.e. key/value pairs) of rules which can be included from other +# places in the grammar. The key is the name of the rule and the value is the actual rule. +repository: + variable-reference: + name: variable.other.cmake + begin: \$(ENV|CACHE)?{ + beginCaptures: + "0": { name: punctuation.definition.variable-ref.start.cmake } + end: "}" + endCaptures: + "0": { name: punctuation.definition.variable-ref.end.cmake } + patterns: + - include: "#variable-reference" + generator-expression: + name: variable.other.cmake + begin: \$\< + beginCaptures: + "0": { name: punctuation.definition.generator-exp.start.cmake } + end: \> + endCaptures: + "0": { name: punctuation.definition.generator-exp.end.cmake } + patterns: + - include: "#generator-expression" +patterns: + # bracket comment + - name: comment.block.bracket.cmake + begin: '#\[(=*)\[' + end: \]\1\] + # line comment + - name: comment.line.number-sign.cmake + match: "#(.*$)" + # string + - name: string.quoted.double.cmake + begin: '"' + end: '"' + patterns: + - include: "#variable-reference" + - include: "#generator-expression" + - name: constant.character.escape.cmake # escaped character in quoted string + match: \\. + # bracket argument + - name: meta.function.variable.parameter.cmake + begin: \s+\[(=*)\[ + end: \]\1\] + # variable reference + - include: "#variable-reference" + # generator expression + - include: "#generator-expression" + # numbers + - name: constant.numeric.cmake + match: \b[0-9\.]+\b + # bool constant + - name: constant.language.bool.cmake + match: \b(?i:ON|YES|TRUE|Y|OFF|NO|FALSE|N|IGNORE|NOTFOUND|\w*-NOTFOUND)\b + # Deprecated Commands + - name: invalid.deprecated.command.cmake + match: ^\s*\b(?i:build_name|exec_program|export_library_dependencies|install_files|install_programs|install_targets|load_command|make_directory|output_required_files|qt_wrap_cpp|qt_wrap_ui|remove|subdir_depends|subdirs|use_mangled_mesa|utility_source|variable_requires|write_file)\b + # flow control commands in cmake + - name: keyword.control.cmake + match: ^\s*\b(if|elif|else|endif|while|endwhile|break|continue|foreach|endforeach|return)\b + # unary tests operators + - name: keyword.operator.unary.cmake + match: \b(EXISTS|COMMAND|DEFINED)\b + # binary tests operators + - name: keyword.operator.binary.cmake + match: \b(EQUAL|LESS|LESS_EQUAL|GREATER|GREATER_EQUAL|STREQUAL|STRLESS|STRLESS_EQUAL|STRGREATER|STRGREATER_EQUAL|VERSION_EQUAL|VERSION_LESS|VERSION_LESS_EQUAL|VERSION_GREATER|VERSION_GREATER_EQUAL|MATCHES)\b + # logical operators + - name: keyword.operator.logical.cmake + match: \b(NOT|AND|OR)\b + # CACHE variable type + - name: entity.name.type.cmake + match: \b(BOOL|FILEPATH|PATH|STRING|INTERNAL)\b + # variable declaration + - name: entity.name.function.cmake + - match: \b(?i:(set)\s*\(\s*(\w+)\s+) + captures: + "1": { name: entity.name.function.cmake } + "2": { name: variable.cmake } + # cmake builtin functions + - name: entity.name.function.cmake + match: \b(?i:cmake_host_system_information|cmake_language|cmake_minimum_required|cmake_parse_arguments|cmake_path|cmake_policy|configure_file|endfunction|endmacro|execute_process|file|find_file|find_library|find_package|find_path|find_program|function|get_cmake_property|get_directory_property|get_filename_component|get_property|include|include_guard|list|macro|mark_as_advanced|math|message|option|separate_arguments|set|set_directory_properties|set_property|site_name|string|unset|variable_watch|add_compile_definitions|add_compile_options|add_custom_command|add_custom_target|add_definitions|add_dependencies|add_executable|add_library|add_link_options|add_subdirectory|add_test|aux_source_directory|build_command|create_test_sourcelist|define_property|enable_language|enable_testing|export|fltk_wrap_ui|get_source_file_property|get_target_property|get_test_property|include_directories|include_external_msproject|include_regular_expression|install|link_directories|link_libraries|load_cache|project|remove_definitions|set_source_files_properties|set_target_properties|set_tests_properties|source_group|target_compile_definitions|target_compile_features|target_compile_options|target_include_directories|target_link_directories|target_link_libraries|target_link_options|target_precompile_headers|target_sources|try_compile|try_run|ctest_build|ctest_configure|ctest_coverage|ctest_empty_binary_directory|ctest_memcheck|ctest_read_custom_files|ctest_run_script|ctest_sleep|ctest_start|ctest_submit|ctest_test|ctest_update|ctest_upload)\b diff --git a/syntaxes/cmakecache.tmLanguage.yml b/syntaxes/cmakecache.tmLanguage.yml new file mode 100644 index 0000000..26d7363 --- /dev/null +++ b/syntaxes/cmakecache.tmLanguage.yml @@ -0,0 +1,48 @@ +fileTypes: + - cmakecache + - CMakeCache.txt +scopeName: source.cmakecache +repository: + variable-reference: + name: variable.other.cmake + begin: \$(ENV|CACHE)?{ + beginCaptures: + "0": { name: punctuation.definition.variable-ref.start.cmake } + end: "}" + endCaptures: + "0": { name: punctuation.definition.variable-ref.end.cmake } + patterns: + - include: "#variable-reference" +patterns: + # comment line start with # + - name: comment.line.number-sign.cmakecache + match: "#.*$" + # comment line start with // + - name: comment.line.double-slash.cmakecache + match: //.*$ + - name: asign.cmakecache + match: '\s*([\w-]+):(\w+)(=)(.+)?' + captures: + "1": { name: variable.cmakecache } # cache variable name + "2": { name: entity.name.type.cmakecache } # cache variable type + "3": { name: asign.cmakecache } # = + # right-hand side of the = + "4": + name: value.cmakecache + # https://macromates.com/manual/en/language_grammars + # The value of these keys is a dictionary with the key being the capture + # number and the value being a dictionary of attributes to assign to the + # captured text. Currently name is the only attribute supported. + # 上面的链接中的文档说 captures 目前只支持 name 属性,实际上也支持 patterns 属性 + patterns: + - name: string.quoted.double.cmake + begin: '"' + end: '"' + patterns: + - include: "#variable-reference" + - name: constant.character.escape.cmake # escaped character in quoted string + match: \\. + - name: constant.numeric.cmakecache + match: \b[0-9\.]+\b + - name: constant.language.bool.cmakecache + match: \b(?i:\w*-NOTFOUND|NOTFOUND|ON|YES|TRUE|Y|OFF|NO|FALSE|N|IGNORE)$