Lsp: Figure out how to allow servers to start for files outside of project

Created on 29 Apr 2020  路  30Comments  路  Source: sublimelsp/LSP

With the emergence of LSP-bash, I have now realized we need to be able to start servers for files outside of the project directory. That's because I don't keep a project for my bash scripts in ~/ and, if I would, that would basically kill ST.

I haven't put any thought in it yet but there must be some way to handle that. Maybe decide per-server?

investigation

Most helpful comment

Have been refer to this issue from https://github.com/sublimelsp/LSP/issues/1013.
Though not sure how

 Your particular linter settings for your project might not apply to other projects.

relates to my problem.

I don't really care for linter, as is flagging programming errors, bugs, etc.
I only care about being able to follow "go to definition" path further on around Go
standard library.

Edit: I would just argue that this particular use case is for easier understanding the code by finding relevant definitions, not for vetting it.

All 30 comments

We may want a boolean for this in ClientConfig.

Have been refer to this issue from https://github.com/sublimelsp/LSP/issues/1013.
Though not sure how

 Your particular linter settings for your project might not apply to other projects.

relates to my problem.

I don't really care for linter, as is flagging programming errors, bugs, etc.
I only care about being able to follow "go to definition" path further on around Go
standard library.

Edit: I would just argue that this particular use case is for easier understanding the code by finding relevant definitions, not for vetting it.

How about this: we start/assign-server-to-view anywhere, but we only show diagnostics for views inside the workspace?

For LSP-bash I would expect to see diagnostics regardless (assuming that it has those).

My opinion of this is that there should indeed be a way to use LSP on single files. It's actually not that uncommon for me to browse files that are unrelated to any project and having at least some subset of features would be nice.

The only stipulation is that the LSP session needs to tell the server that we're dealing with a single file so some restrictions can be lifted on the syntax parser.

The only stipulation is that the LSP session needs to tell the server that we're dealing with a single file.

I believe that should be clear for the servers that support workspaceFolders at least.

    /**
     * The workspace folders configured in the client when the server starts.
     * This property is only available if the client supports workspace folders.
     * It can be `null` if the client supports workspace folders but none are
     * configured.
     *
     * @since 3.6.0
     */
    workspaceFolders?: WorkspaceFolder[] | null;

Or actually, I'm not sure what would happen if you would open file from outside the workspace (project) in an already open workspace (project). Will we then send workspaceFolders or not?

Can rootURI/rootPath just be a file? That would be easy to determine. I assume however that each single file will be its own LSP session which contains an initialization message.

That would be against the spec I think as it specifies that it should be a workspace URI.

Also, that would mean that LSP would have to start separate server instance for each file outside the workspace. So no, I don't think it can be a file.

workspaceFolders is probably the way to go. And I'm pretty sure LSP would not set it for files outside of workspace.

I only care about being able to follow "go to definition" path further on around Go
standard library.

Is this perhaps a server specific or configuration issue? It works relatively well for me with the server for the Julia language (sometimes the server crashes, but probably unrelated to the LSP package):

goto_symbol

I also often open single files without a Sublime project, and all the language servers I use (HTML, CSS, LaTeX, Python, Java, PowerShell, Fortran, Julia....) seem to have no problem with that, so I must admit that I don't really have a clue what most of this issue/thread here is about :see_no_evil:

workspaceFolders is probably the way to go. And I'm pretty sure LSP would not set it for files outside of workspace.

Is the plan to keep one LSP session running? If not then when you send the initialize message just leave rootPath a null, which the spec says is ok.

workspaceFolders is probably the way to go. And I'm pretty sure LSP would not set it for files outside of workspace.

Is the plan to keep one LSP session running? If not then when you send the initialize message just leave rootPath a null, which the spec says is ok.

I suppose it would be null, yes. And there would be at most one session for all files outside of workspace.

But then the server doesn't know what file it's working with. The spec just says "The rootUri of the workspace." Why can't that be a file?

For it to be a file, LSP would have to start one server per-file as it's initializeParams so only sent on starting the server.

Sorry I thought that's what you said was happening anyways. Is that not feasible? Maybe starting a new server is too expensive in terms of resources?

Starting one instance of server for each file is IMO unreasonable.

And what would be the benefit of that for the user really?

It sounds like you just want that from the perspective of your server where you haven't implemented workspaceFolders yet? Is that the reason? Or why are you ignoring the suggestion of using workspaceFolders for that?

But can we send a document path in workspaceFolders? This may be ST specific thing (which I'm happy with personally) which other servers won't know what to do with.

So the reason we are discussing this is this statement of yours, yes?

The only stipulation is that the LSP session needs to tell the server that we're dealing with a single file so some restrictions can be lifted on the syntax parser.

So would it not work for you if a single instance of server was created for all files that are opened from outside of the workspace? The you don't have workspace folders so you know that those files are outside. And you get notification about opening files with textDocument/didOpen so you know document path from that.

I also often open single files without a Sublime project, and all the language servers I use (HTML, CSS, LaTeX, Python, Java, PowerShell, Fortran, Julia....) seem to have no problem with that, so I must admit that I don't really have a clue what most of this issue/thread here is about 馃檲

If you open those files in a separate (empty) window then it will work.
If you open those files in an already open project with folders then server won't run for those files.

I believe that's the deal. Might be wrong as I don't have full overview right now. And it might depend on whether server supports workspace folders.

So would it not work for you if a single instance of server was created for all files that are opened from outside of the workspace? The you don't have workspace folders so you know that those files are outside. And you get notification about opening files with textDocument/didOpen so you know document path from that.

I may just be confused because I entered this conversion late. When I open a file outside the workspace in an existing LSP session why doesn't LSP send a textDocument/didOpen and then subsequent requests? That's basically what I want.

As for single files outside any LSP session I think you said then it would start a new session.

There seems to be a lot of confusion here. Let me explain what happens.

In the ST world we have the following key functions of a window:

  • window.folders()
  • window.project_data()
  • window.project_file_name()

ST can be in one of three modes:

  1. "single file mode": window.folders() is empty.
  2. "single folder mode": window.folders() is a list with one entry.
  3. "project mode": window.folders() has one or more entries, window.project_file_name() is a non-empty string.

You can test the various modes more easily by setting "hot_exit": true in your Preferences.sublime-settings, otherwise a lot of session caching takes place and it's hard to get "out" of folder/project mode to test single-file mode. Alternatively you may delete $DATA/Local/Session.sublime_session as well as any .sublime-workspace file you have lying around when working with a .sublime-project.

In the LSP world there are two types of language servers:

  • servers with the capability path workspace.workspaceFolders.supported
  • servers without the capability path workspace.workspaceFolders.supported

When dealing with a language server with capability path workspace.workspaceFolders.supported:

  • window.folders() is mirrored exactly like the workspaceFolders in the initialize request.
  • When we're in single-file mode we send "workspaceFolders": null in the initialize request.
  • Any file, whether inside or outside the project, can be handled by this language server.

Thus, only one process of such a language server will ever exist per window. This is all nice and good.

Matters are complicated when dealing with a language server that doesn't have workspace.workspaceFolders.supported.

  • We still mirror window.folders() with the workspaceFolders in the initialize request because we don't know whether a server supports workspaces or not, we will only know this once it responds to the initialize request.
  • If the server doesn't support workspaces, then we remember only the workspace of the view that initiated the server process. (the rootUri basically)
  • When opening another view we have to check whether we can assign an existing server session to that view or whether we need to start a new one. When the view is inside the project this is doable. When the view is outside the project we used to assign the first workspace-unaware server to that view.

Anyway there's no technical objection to serving views outside the project. It might receive the wrong session for workspace-unaware servers (the first session, but it might have needed the second session). But to that I say: make your server workspace-aware to avoid all this.

the question I have then is, why don't I get LSP messages when I open an unrelated file into a LSP enabled workspace window? (by say dragging and dropping it in the tab bar).

Because that's a deliberate choice. The spec says nothing about this. But we're here to discuss it :)

Wow, I really derailed your conversation here. :) If it was just a deliberate choice then my preference is to merely send the LSP messages as normal, regardless of where the file came from. Most features will work and for the ones that are questionable (like workspace symbols) the server can decide for itself what to do.

So to summarize, just treat them like any other files. I see no problem with this whatsoever and I'm kind of confused now as to why this wasn't always the default behavior.

Servers like eslint or typescript will show a lot of errors in those cases as project configuration files are not found. That's why it's like it is now. So it might make sense to toggle this per-server and hence this issue.

Ah, I get it. My server won't care so I think this should be a per-server option.

coc.nvim use rootPatterns to resolve workspaceFolder for files outside of project.

https://github.com/neoclide/coc.nvim/blob/164bd6b6113eecc52ae193966783a453bcc04158/src/workspace.ts#L1592-L1598

For go, below is the traffics between gopls and coc.nvim:

[Trace - 8:57:13 鈹淔10: PM鈹 Sending request 'textDocument/definition - (5)'.
Params: {
    "textDocument": {
        "uri": "file:///d%3A/app/main.go"
    },
    "position": {
        "line": 122,
        "character": 9
    }
}


[Trace - 8:57:13 鈹淔10: PM鈹 Received response 'textDocument/definition - (5)' in 1ms.
Result: [
    {
        "uri": "file:///C:/go/src/flag/flag.go",
        "range": {
            "start": {
                "line": 580,
                "character": 4
            },
            "end": {
                "line": 580,
                "character": 9
            }
        }
    }
]


[Trace - 8:57:13 鈹淔10: PM鈹 Sending notification 'workspace/didChangeWorkspaceFolders'.
Params: {
    "event": {
        "added": [
            {
                "uri": "file:///c%3A/Go/src",
                "name": "src"
            }
        ],
        "removed": []
    }
}

After that, no extra gopls server will be started for packages under $GOROOT, and the problem in #1013 will not be a problem.

There has go.mod under C:/Go/src, so the workspaceFolder resolved for C:/go/src/flag/flag.go is C:/Go/src.
https://github.com/josa42/coc-go/blob/master/package.json#L51-L57

Sorry for comment on closed issue but is this resolved?
Last comment from @wsw0108 that https://github.com/sublimelsp/LSP/issues/1013 should not be a problem but I am not sure how make it a no problem? :)

@rwols

Sorry for comment on closed issue but is this resolved?

For ST4, yes: https://github.com/sublimelsp/LSP/releases/tag/4070-1.0.13

For ST3, no.

So this is gonna be only ST4 feature?

I forgot about this feature being added. How does it work though? I opened a single file and enabled LSP for the project but I don't see any output in the LSP log panel.

Was this page helpful?
0 / 5 - 0 ratings