For most large projects, one will have multiple sub projects. LSP calls it workspaceFolders. https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders
VSCode has something similar. https://code.visualstudio.com/docs/editor/multi-root-workspaces. They also have .code-workspace and one can load the file.
{
"folders": [
{
// Source code
"name": "Product",
"path": "vscode"
},
{
// Docs and release notes
"name": "Documentation",
"path": "vscode-docs"
},
{
// Yeoman extension generator
"name": "Extension generator",
"path": "vscode-generator-code"
}
]
}
Would it be possible to have this api so multiple plugins can work together instead of each plugin having concept of workspace folder. Lot of LSP servers are not interacting with workspace folders so would be good to have this api.
Examples:
We would need apis to open/close/add folder(s)/remove folder(s) along with events so others can watch for changes. We don't need to support the .code-workspace file as one can easily build a plugin based on this apis. We could make this play nicely with cwd.
I see that almost all LSP plugins have built their own standard concept of workspace folders.
One thing to also note is that some languages such as C# uses files for projects instead of folders but new c# is moving to folders. we could call it workspace items instead have an item be a folder or string.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.![]()
For most large projects, one will have multiple sub projects. LSP calls it workspaceFolders. https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_workspaceFolders
VSCode has something similar. https://code.visualstudio.com/docs/editor/multi-root-workspaces. They also have
.code-workspaceand one can load the file.{ "folders": [ { // Source code "name": "Product", "path": "vscode" }, { // Docs and release notes "name": "Documentation", "path": "vscode-docs" }, { // Yeoman extension generator "name": "Extension generator", "path": "vscode-generator-code" } ] }Would it be possible to have this api so multiple plugins can work together instead of each plugin having concept of workspace folder. Lot of LSP servers are not interacting with workspace folders so would be good to have this api.
Examples:
- a mono repo where the lib may have different eslint settings and test project may have different eslint settings.
- find/grep only inside a test project/folder and not the lib folder.
We would need apis to open/close/add folder(s)/remove folder(s) along with events so others can watch for changes. We don't need to support the
.code-workspacefile as one can easily build a plugin based on this apis. We could make this play nicely withcwd.
By open I meant Open workspace, while add I meant Add folder to workspace.
Ideally open could take like of folders as an arg and in the future we might also add a folder without reloading the entire workspace.
By remove I meant Remove folder from workspace, while close I meant Close workspace.
open workspace vs add folder to workspace example in vscode:
—
You are receiving this because you commented.
not familiar with vsc too much, but i guess that workspace meant like different vim instance, then that can started diff lsp server, i guess so.. or it may messed still..
// btw: lsp really can omni very good on e.g bash/viml etc langs? :-)
—
You are receiving this because you commented.
Here is the gist of workspace apis that I have gathered for VsCode. It doesn't support multiple workspace but there has been feature requests for it microsoft/vscode#43188.
interface WorkspaceFolder {
name: string | undefined; // used to refer to this workspace folder in the user interface
uri: Uri;
}
workspacefolders: WorkspaceFolder[];
// returns undefined when the given uri doesn't match any workspace folder
fn getWorkspaceFolder(uri: Uri) -> WorkspaceFolder | undefined
// start: zero based location currently opened list
// deleteCount: optional number of workspace folder to remove
// workspaceFoldersToAddOrUpdate: optional to add in place of the deleted ones, each workspace is identified with mandatory uri or optional name
// true if successful else false (eg. false when 2 folders with same uri added)
fn updateWorkspaceFolders(start: number, deleteCount: number | undefined | null, workspaceFoldersToAddOrUpdate: WorkspaceFolder[]): boolean;
interface WorkspaceFolderChangeEvent {
added: WorkspaceFolder[];
removed: WorkspaceFolder[];
}
event onDidChangeWorkspaceFolder(e: Event<WorkspaceFolderChangeEvent>)
Here is my proposal for vim's workspace folder. This supports multiple workspace and WorkspaceFolder is called Project instead.
interface Workspace {
name: string;
path?: string;
}
interface Project {
name: string;
path?: string;
}
augroup workspace
autocmd!
autocmd WorkspaceChangePre * call s:workspace_change_pre()
autocmd WorkspaceChangePost * call s:workspace_change_post()
augroup END
function! s:workspace_change_pre() abort
let s:old_workspaces = workspace_getall()
let s:old_cur = workspace_getcur() " { 'workspace_id': '', 'project': '' }
endfunction
function! s:workspace_change_post() abort
" can diff with s:old_workspaces
let l:new_workspaces = workspace_getall()
unlet s:old_workspaces
let s:cur = workspace_getcur() " { 'workspace_id': '', 'project': '' }
endfunction
let workspace_id = workspace_create({ 'name': 'myproject' 'path': '~/code/myproject' })
let workspace = workspace_getoptions(workspace_id) " { 'name': 'myproject', 'path': '~/code/myproject' }
call workspace_setoptions(workspace_id, {
\ 'projects': [
\ { 'name': 'server', 'path': '~/code/myproject/server' },
\ { 'name': 'client', 'path': '~/code/myproject/client' },
\ ]
\ })
call workspace_setcur(workspace_id, 'server')
call workspace_close(workspace_id)
let curr = workspace_getcur() " { 'workspace_id': 1, 'project': 'server' }
Some languages supports files as project which may be under the same folder such as in C# where I could have MyProject.csproj, MyProject.Tests.csproj and so on hence I used the term project instead of workspace folders.
It might also be good to support reload event with call workspace_reload(workspace_id) or call workspace_reload('workspace_id', ['project_name']). All it does is fires events but allows plugins to work together.
Could change workspace_getall to workspace_get and support overloads instead. Another option is to just get the list of ids but I don't know if there is an existing api pattern.
let all_workspaces = workspace_get()
let one_workspace = workspace_get(workspace_id)
Thoughts on this api?
—
You are receiving this because you commented.
I do like the idea of letting vim define an API here - that allows for separating the responsibilities of managing or discovering workspace folders from the responsibility of reporting it to the server. One of the things I am struggling with in vim-lsc is not knowing how users or language servers would prefer to configure the behavior - trying to generalize across different preferences seems like too much complexity for a single plugin. Having a shared interface defined by vim could let us split that up.
—
You are receiving this because you commented.
Another simpler option could be to possible port neovim's dictionary watcher dictwatcheradd and dictwatcherdel(). neovim/neovim#3603.
dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()*
Adds a watcher to a dictionary. A dictionary watcher is
identified by three components:
- A dictionary({dict});
- A key pattern({pattern}).
- A function({callback}).
After this is called, every change on {dict} and on keys
matching {pattern} will result in {callback} being invoked.
For example, to watch all global variables: >
silent! call dictwatcherdel(g:, '*', 'OnDictChanged')
function! OnDictChanged(d,k,z)
echomsg string(a:k) string(a:z)
endfunction
call dictwatcheradd(g:, '*', 'OnDictChanged')
<
For now {pattern} only accepts very simple patterns that can
contain a '*' at the end of the string, in which case it will
match every key that begins with the substring before the '*'.
That means if '*' is not the last character of {pattern}, only
keys that are exactly equal as {pattern} will be matched.
The {callback} receives three arguments:
- The dictionary being watched.
- The key which changed.
- A dictionary containing the new and old values for the key.
The type of change can be determined by examining the keys
present on the third argument:
- If contains both `old` and `new`, the key was updated.
- If it contains only `new`, the key was added.
- If it contains only `old`, the key was deleted.
This function can be used by plugins to implement options with
validation and parsing logic.
dictwatcherdel({dict}, {pattern}, {callback}) *dictwatcherdel()*
Removes a watcher added with |dictwatcheradd()|. All three
arguments must match the ones passed to |dictwatcheradd()| in
order for the watcher to be successfully deleted.
Configs can then just be part of dictionary like how most plugin authors already have. This would also help support dynamic registration for LSP too.
Good part about dictwatcher is that it can first be implemented on user space and then in future if we think we have better understanding of workspace apis we can create a dictionary such as v:workspace or g:workspace with a special structure.
—
You are receiving this because you commented.