diff --git a/LSP-json.schema.json b/LSP-json.schema.json new file mode 100644 index 0000000..aa1fbb8 --- /dev/null +++ b/LSP-json.schema.json @@ -0,0 +1,58 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/product.schema.json", + "title": "LSP-json settings", + "description": "Configure the LSP-json settings", + "type": "object", + "definitions": { + "language": { + "type": "object", + "additionalProperties": false, + "properties": { + "languageId": { + "type": "string", + "description": "See a list of available languages Ids in https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers" + }, + "scopes": { + "description": "Array of scopes that will trigger the activation of the language server. Open `Tools/Developer/Show scope name` menu to get the scope name.", + "type": "array", + "items": { + "description": "Open `Tools/Developer/Show scope name` menu to get the scope name", + "type": "string" + } + }, + "syntaxes": { + "description": "Array of sublime-syntaxt strings. Run `view.settings().get('syntax')` in sublime console to get the syntax.", + "type": "array", + "items": { + "description": "Run `view.settings().get('syntax')` in sublime console to get the syntax.", + "type": "string" + } + } + }, + "required": ["languageId", "scopes", "syntaxes"] + } + }, + "properties": { + "client": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "languages": { + "type": "array", + "items": { + "$ref": "#/definitions/language" + } + }, + "initializationOptions": { + "type": "object" + }, + "settings": { + "type": "object" + } + } + } + } +} diff --git a/LSP-json.sublime-settings b/LSP-json.sublime-settings index a946be5..afd45b1 100644 --- a/LSP-json.sublime-settings +++ b/LSP-json.sublime-settings @@ -9,6 +9,15 @@ "Packages/JavaScript/JSON.sublime-syntax", "Packages/JSON/JSON.sublime-syntax" ] + }, + { + "languageId": "jsonc", + "scopes": [ + "source.jsonc" + ], + "syntaxes": [ + "Packages/LSP-json/syntax/JSONC.sublime-syntax" + ] } ], "initializationOptions": { diff --git a/plugin.py b/plugin.py index 4b8ce84..8b57114 100644 --- a/plugin.py +++ b/plugin.py @@ -3,6 +3,7 @@ import sublime import threading import subprocess +import json from LSP.plugin.core.handlers import LanguageHandler from LSP.plugin.core.settings import ClientConfig, LanguageConfig, read_client_config @@ -12,7 +13,36 @@ server_path = os.path.join(package_path, 'node_modules', 'vscode-json-languageserver', 'bin', 'vscode-json-languageserver') +def get_plugin_schemas(): + plugin_schemas = [] + added_schemas = [] + + for schema in sublime.find_resources("*.schema.json"): + schema_content = sublime.load_resource(schema) + schema_file_name = os.path.basename(schema) + + # Associate a `LSP-json.schema.json` file to `LSP-json.sublime-settings` + settings_file_name = schema_file_name.replace(".schema.json", ".sublime-settings") + try: + if schema_file_name in added_schemas: + continue + + added_schemas.append(schema_file_name) + plugin_schemas.append({ + "name": schema_file_name, + "fileMatch": [ + settings_file_name + ], + "schema": json.loads(schema_content) + }) + except Exception as e: + pass + + return plugin_schemas + + def plugin_loaded(): + get_plugin_schemas() is_server_installed = os.path.isfile(server_path) print('LSP-json: Server {}.'.format('installed' if is_server_installed else 'is not installed' )) @@ -74,6 +104,12 @@ def name(self) -> str: def config(self) -> ClientConfig: settings = sublime.load_settings("LSP-json.sublime-settings") client_configuration = settings.get('client') + + all_schemas = [] + plugin_schemas = get_plugin_schemas() + all_schemas.extend(plugin_schemas) + all_schemas.extend(schemas) + default_configuration = { "command": [ 'node', @@ -88,11 +124,18 @@ def config(self) -> ClientConfig: "Packages/JavaScript/JSON.sublime-syntax", "Packages/JSON/JSON.sublime-syntax" ] + }, + { + "languageId": "jsonc", + "scopes": ["source.jsonc"], + "syntaxes": [ + "Packages/LSP-json/syntax/JSONC.sublime-syntax" + ] } ] } default_configuration.update(client_configuration) - default_configuration['settings']['json']['schemas'] = schemas + default_configuration['settings']['json']['schemas'] = all_schemas return read_client_config('lsp-json', default_configuration) def on_start(self, window) -> bool: diff --git a/syntax/JSONC.sublime-syntax b/syntax/JSONC.sublime-syntax new file mode 100644 index 0000000..0623562 --- /dev/null +++ b/syntax/JSONC.sublime-syntax @@ -0,0 +1,143 @@ +%YAML 1.2 +--- +name: JSONC +file_extensions: + - jsonc + - sublime-settings + - sublime-menu + - sublime-keymap + - sublime-mousemap + - sublime-theme + - sublime-build + - sublime-project + - sublime-completions + - sublime-commands + - sublime-macro + - sublime-color-scheme + - ipynb + - Pipfile.lock +scope: source.jsonc +contexts: + prototype: + - include: comments + main: + - include: value + value: + - include: constant + - include: number + - include: string + - include: array + - include: object + array: + - match: '\[' + scope: punctuation.section.sequence.begin.jsonc + push: + - meta_scope: meta.sequence.jsonc + - match: '\]' + scope: punctuation.section.sequence.end.jsonc + pop: true + - include: value + - match: "," + scope: punctuation.separator.sequence.jsonc + - match: '[^\s\]]' + scope: invalid.illegal.expected-sequence-separator.jsonc + comments: + - match: /\*\*(?!/) + scope: punctuation.definition.comment.jsonc + push: + - meta_scope: comment.block.documentation.jsonc + - meta_include_prototype: false + - match: \*/ + pop: true + - match: ^\s*(\*)(?!/) + captures: + 1: punctuation.definition.comment.jsonc + - match: /\* + scope: punctuation.definition.comment.jsonc + push: + - meta_scope: comment.block.jsonc + - meta_include_prototype: false + - match: \*/ + pop: true + - match: (//).*$\n? + scope: comment.line.double-slash.js + captures: + 1: punctuation.definition.comment.jsonc + constant: + - match: \b(?:true|false|null)\b + scope: constant.language.jsonc + number: + # handles integer and decimal numbers + - match: -?(?:0|[1-9]\d*)(?:(?:(\.)\d+)(?:[eE][-+]?\d+)?|(?:[eE][-+]?\d+)) + scope: constant.numeric.float.decimal.jsonc + captures: + 1: punctuation.separator.decimal.jsonc + - match: -?(?:0|[1-9]\d*) + scope: constant.numeric.integer.decimal.jsonc + object: + # a jsonc object + - match: '\{' + scope: punctuation.section.mapping.begin.jsonc + push: + - meta_scope: meta.mapping.jsonc + - match: '\}' + scope: punctuation.section.mapping.end.jsonc + pop: true + - match: '"' + scope: punctuation.definition.string.begin.jsonc + push: + - clear_scopes: 1 + - meta_scope: meta.mapping.key.jsonc string.quoted.double.jsonc + - meta_include_prototype: false + - include: inside-string + - match: ":" + scope: punctuation.separator.mapping.key-value.jsonc + push: + - match: ',|\s?(?=\})' + scope: invalid.illegal.expected-mapping-value.jsonc + pop: true + - match: (?=\S) + set: + - clear_scopes: 1 + - meta_scope: meta.mapping.value.jsonc + - include: value + - match: '' + set: + - match: ',' + scope: punctuation.separator.mapping.pair.jsonc + pop: true + - match: \s*(?=\}) + pop: true + - match: \s(?!/[/*])(?=[^\s,])|[^\s,] + scope: invalid.illegal.expected-mapping-separator.jsonc + pop: true + - match: '[^\s\}]' + scope: invalid.illegal.expected-mapping-key.jsonc + string: + - match: '"' + scope: punctuation.definition.string.begin.jsonc + push: inside-string + inside-string: + - meta_scope: string.quoted.double.jsonc + - meta_include_prototype: false + - match: '"' + scope: punctuation.definition.string.end.jsonc + pop: true + - include: string-escape + - match: $\n? + scope: invalid.illegal.unclosed-string.jsonc + pop: true + string-escape: + - match: |- + (?x: # turn on extended mode + \\ # a literal backslash + (?: # ...followed by... + ["\\/bfnrt] # one of these characters + | # ...or... + u # a u + [0-9a-fA-F]{4} # and four hex digits + ) + ) + scope: constant.character.escape.jsonc + - match: \\. + scope: invalid.illegal.unrecognized-string-escape.jsonc