Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions LSP-json.schema.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
}
9 changes: 9 additions & 0 deletions LSP-json.sublime-settings
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
45 changes: 44 additions & 1 deletion plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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' ))

Expand Down Expand Up @@ -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',
Expand All @@ -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:
Expand Down
143 changes: 143 additions & 0 deletions syntax/JSONC.sublime-syntax
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
%YAML 1.2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of this sublime-syntax file?

Copy link
Contributor

@jfcherng jfcherng Feb 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think He doesn't want this plugin to depend on PackageDev so he creates it for sublime-related files. Otherwise, users can directly use #6 (comment) in their settings to activate LSP-json.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is the point.

LSP-json will have its own JSONC syntax file that will come with this package, and will be assigned to all sublime, settings, theme, completions, keymaps, files... So no need to have 10 different scopes in the config for jsonc. There will be just one and that is source.jsonc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like it :( The PackageDev syntaxes have great highlighting. What's more is that there is already a syntax for JSON with comments at Packages/JavaScript/JSON.sublime-syntax.
Why does this plugin need to depend on PackageDev in the first place?
Also, why not write a language server for json-with-comments?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not put jsonc instead of json for the languageId? Wouldn't that make the json-language-server shut up about comments? https://github.com/sublimelsp/LSP-json/blob/master/plugin.py#L85

https://github.com/vscode-langservers/vscode-json-languageserver#server-capabilities

jsonc documents additionally accept single line (//) and multi-line comments (/* ... */).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now that this pull request modifies the languageId from json to jsonc. So that should already remove the errors about comments. So then why not use Packages/JavaScript/JSON.sublime-syntax? Remember that maintaining a custom JSON syntax is some serious maintainer burden...

Copy link
Contributor

@jfcherng jfcherng Feb 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember that maintaining a custom JSON syntax is some serious maintainer burden...

I don't like this solution either (maybe because I have PackageDev installed :D ), but...

So then why not use Packages/JavaScript/JSON.sublime-syntax?

ST's JSON doesn't differentiate json and jsonc since it treats everything as jsonc. If we do so, LSP will not treat a comma (and comment) in a JSON file as an error. If we want a way to differentiate them, then either via a 3rd-party (PackageDev) or creates our own...


Another way to not actually maintain the syntax "definition" is something like:

%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: Packages/JavaScript/JSON.sublime-syntax#prototype
  main:
    - include: Packages/JavaScript/JSON.sublime-syntax#main

It results in a base scope source.jsonc with other ....json subscopes.

image

But there is no guarantee that ST will chooses your syntax definition over PackageDev's (or other 3rd-party plugin's). I don't know how ST chooses the syntax when there are multiple qualified candidates.

---
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