Support reading schema files from installed packages
rchl opened this issue · comments
Implement fetching schema files from installed packages that have defined their custom schemas for preferences (and possibly for other types of configurations too).
For example, if package LSP
wants to provide schema for its LSP.sublime-settings
preferences it can include file LSP.sublime-settings.schema.json
(or just LSP.sublime-settings.schema
but then the one developing the schema would have to manually associate that extension with JSON syntax) that this package will find and automatically map to LSP.sublime-settings
file pattern.
The limitation of that system is that one schema can only be associated with one file pattern.
(There could be a special case for preferences where we would also auto-create patterns with platform suffixes.)
@predragnikolic @rwols Thoughts (especially on naming)?
How about this one?
If we have a file like LSP-json.sublime-settings
the schema would be LSP-json.schema.json
The limitation of that system is that one schema can only be associated with one file pattern.
I am kind of ok with that limitation.
There is a way that a schema can reference another schema.
Because sublime plugins are zipped, we cannot access files like this.
Unfortunately this approach doesn't work either:
But we could upload the shared schema on GitHub for example and use it?
I am just listing all possible approaches :)
+1 for LSP.sublime-settings.schema.json
but actually ${file_name}.schema.json
rather than ${plugin_name}.sublime-settings.schema.json
.
Note that some plugins may have multiple settings files.
For example, BracketHighlighter
has 3 settings files
bh_swapping.sublime-settings
bh_wrapping.sublime-settings
bh_core.sublime-settings
There is a way that a schema can reference another schema.
...
Unfortunately this approach doesn't work either:
What use case are you thinking of here when giving an example for referencing another schema?
For the case of sublime-settings it shouldn't be needed to reference the base one as JSON server merges them as long as both match the current file.
So given that we have sublime://schemas/sublime-settings
schema with *.sublime-settings
file pattern, that one will apply to all other settings and doesn't have to be referenced.
+1 for
LSP.sublime-settings.schema.json
but actually${file_name}.schema.json
rather than${plugin_name}.sublime-settings.schema.json
.
+1
So If I understand @jfcherng's idea correctly, if a user opens a file
/path/to/Packages/User/bh_swapping.sublime-settings
Then vscode-json-languageserver will request a schema for that file. We then respond with
bh_swapping.schema.json
vscode-json-languageserver then asks for this file, and we do a
schemas = sublime.find_resources('bh_swapping.schema.json')
If BracketHighlighter then has a file in its zip, say
Packages/BracketHighlighter/Schemas/bh_swapping.schema.json
Then sublime.find_resources('bh_swapping.schema.json')
would contain Packages/BracketHighlighter/Schemas/bh_swapping.schema.json
, hopefully as a first match. So we load it with
sublime.load_resource(schemas[0])
and return the content to vscode-json-languageserver. Sounds like a plan :)
Not exactly - the server won't ask for schema content unless we first associate that file path with schema URL.
So we would have to find all *.schema.json
files up-front (for example on first initialization of the server so that we don't do it on the start of ST) and associate them with the respective settings files.
For example, we would find Packages/LSP/LSP.sublime-settings.schema.json
and create a mapping for it:
{
'LSP.sublime-settings.json': [
'sublime://Packages/LSP/LSP.sublime-settings.schema.json`
]
}
After we do that, the server will ask for content of that sublime://Packages/LSP/LSP-sublime-settings.schema.json
schema URL, on opening a given settings file, and we would do the loading of that schema at this point and return to the server.
Note: The mapping might need to be more specific to avoid mixing settings of different packages when they are using Default.sublime-settings
name. I hope the path match can include directories...
I see. Presumably you're talking about this line?
https://github.com/sublimelsp/LSP-json/blob/master/plugin.py#L64
If so then yes, we'd need to scan with sublime.find_resources('*.schema.json')
up-front.
Yeah, we can do it there if we are able to do everything synchronously. Otherwise there is also a json/schemaAssociations notification that we can send asynchronously after init.
It has to run synchronous or we risk doing a textDocument/didOpen before json/schemaAssociations is sent.
Perhaps I spoke too soon. It might also be possible to notify json/schemaAssociations in on_initialized
.
But in any case, I don't think sublime.find_resources
would take so long that we'd need to spawn a thread for it. Creating the thread itself probably takes longer than that. For py 3.8 we definitely need async
versions of all the callbacks for ServerHandler
/ LanguageHandler
...
It should be OK to send json/schemaAssociations
at any point after init. Server should react with updated diagnostics. That's the point of that notification in VSCode, to be able to asynchronously collect all schemas without blocking anything.
Gathering schemas for single <package>.sublime-settings
is one thing, but plugins may also support settings placed to Preferences.sublime-settings in order to make them apply on a view specific or project specific scope.
That means any plugin may provide a scheme for Prerences.sublime-settings with the key/value pairs it wants to add. All of them would need to be merged to one single jsonscheme.
Hence it might make more sense for plugins to provide snippets of key/value pairs to be injected into a scheme rather than complete scheme files.
VS Code does so for language-service settings. The relevant meta information to drive the autocompletion engine ar part of the package.json.
Example:
"contributes": {
"configuration": {
"type": "object",
"title": "XML configuration",
"properties": {
"xml.trace.server": {
"type": "string",
"enum": [
"off",
"messages",
"verbose"
],
"default": "off",
"description": "Traces the communication between VS Code and the XML language server.",
"scope": "window"
},
Maybe we should add support for such a package meta data file as well?
Maybe something like a sublime-package.json with ...
{
"jsonschemas": [
{
"pattern": "Preferences.sublime-settings",
"properties": {
"mypackage.my_setting": {
"type": "string",
"description": "The tooltip",
},
},
},
{
"pattern": "LSP.sublime-settings",
"properties": {
"injected_3rd_party_setting": {
"type": "string",
"description": "The tooltip",
},
},
}
],
}
That sounds quite nice and flexible indeed!
Maybe something like a sublime-package.json with ...
Or just package.json
which is already a de-facto standard apparently.
Anyone working on this?
Tied to pick up this topic several times, but haven't come up with a serious approach. I am stuck with the question of how to map the scheme id's to the schemes provided by 3rd-party packages properly. The sublime://....
scheme points to a path within LSP-json. How to do that for something like sublime://the-package-name.sublime-settings.json
then?
Was thinking about plain res://package/the-package-name.sublime-settings.json
but I find it too plain to be future proof.
Busy with some other tasks atm.
A resource identifier consisting of a package name and the path within the package sounds like a proper way to do it. It should be guaranteed to be unique and identifying the proper package. I wonder why do you think this might not be future proof?
Because res://package/the-package-name.sublime-settings.json
denotes the real resource path within a package while most other official ids denote urls to websites. Even your sublime://
is translated to a real path. VS Code uses ms.aka.net/blabla
as intermediate identifier which is then translated to the real path.
If we use the res://package...
it would stop working as soon as someone renames the package. If a package's structure is refactored the relative path of a scheme within the package might change which also renders the id invalid.
If we use the
res://package...
it would stop working as soon as someone renames the package. If a package's structure is refactored the relative path of a scheme within the package might change which also renders the id invalid.
But the ID of the schema would not need to be stored anywhere, I think. It would be generated automatically from the path on the first initialization of the server. Thus renames or structure changes shouldn't be a problem.
(Unless I'm missing something because my memory of how all this works might be a bit rusty.)
(If we go for having sublime_package.json
for defining schemas then we could also match that with sublime-package://...
URIs.)
Not sure if it is common practice, but it seems to be possible to add an $id: ...
key to a json file, which is picked up by the json-language-server to assign a certain scheme. Such a static assignment would fail upon renames. With proper filename/scheme mappings uppon startup this may not be an issue at all, but who knows whether it would become one in future use cases. Therefore a fixed id would be more safe, I think.
By $id: ...
, did you meant $schema
? 🙂
Honestly, I don't see why the packages should define ID themselves. At least in VSCode packages don't do it and it seems to be fine.
Propably you are right.
But with regards to adding a package path to the $id
, how would you handle multiple packages providing additions for Preferences.sublime-settings
, Default.sublime-keymap
etc. then? We would need a mechanism to merge multiple schemes into one. Defining the path of a static scheme as id would not be useful then.
Even though it might be very very uncommon any plugin could want to inject its own keys into any other settings file.
I'd propably find a scheme id like sublime://<FileName>.sublime-<...>-schema
useful. LSP-json would need to find_resources()
and merge everything it finds into one temporary theme file which then is assigned to <FileName>.sublime-<...>
. This is the static way. Maybe need/want to use some kind of intermediate meta data file to handle such mappings.