-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LSP: New settings option to Disable server and Better Goto Definition for property and file paths (1/2) #2504
Changes from all commits
7b7bc26
99a19c5
8a05a44
a3c6ad8
dd79bb3
aeb6532
ca424a1
4863de8
257daf6
92e616f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,13 @@ | ||
import { Uri, workspace } from 'vscode' | ||
import { | ||
LanguageClient, | ||
LanguageClientOptions, | ||
ServerOptions, | ||
TransportKind | ||
} from 'vscode-languageclient/node' | ||
import { documentSelector, serverModule } from 'dvc-vscode-lsp' | ||
import { readFileSync } from 'fs-extra' | ||
import { ConfigurationChangeEvent, workspace } from 'vscode' | ||
import { Disposable } from '../class/dispose' | ||
import { findFiles } from '../fileSystem/workspace' | ||
import { ConfigKey, getConfigValue } from '../vscode/config' | ||
|
||
export class LanguageClientWrapper extends Disposable { | ||
private client: LanguageClient | ||
|
@@ -17,13 +16,7 @@ export class LanguageClientWrapper extends Disposable { | |
super() | ||
|
||
const clientOptions: LanguageClientOptions = { | ||
documentSelector, | ||
|
||
synchronize: { | ||
fileEvents: workspace.createFileSystemWatcher( | ||
'**/*.{yaml,dvc,dvc.lock,json,toml}' | ||
) | ||
} | ||
documentSelector | ||
} | ||
|
||
this.client = this.dispose.track( | ||
|
@@ -35,31 +28,24 @@ export class LanguageClientWrapper extends Disposable { | |
) | ||
) | ||
|
||
// Start the client. This will also launch the server | ||
this.start() | ||
} | ||
|
||
async start() { | ||
await this.client.start() | ||
|
||
const files = await findFiles('**/*.{yaml,json,py,toml}', '.??*') | ||
|
||
const textDocuments = files.map(filePath => { | ||
const uri = Uri.file(filePath).toString() | ||
const languageId = filePath.endsWith('yaml') ? 'yaml' : 'json' | ||
const text = readFileSync(filePath, 'utf8') | ||
if (!this.languageServerDisabled()) { | ||
// This will also start the server process | ||
await this.client.start() | ||
|
||
return { | ||
languageId, | ||
text, | ||
uri, | ||
version: 0 | ||
} | ||
}) | ||
|
||
await this.client.sendRequest('initialTextDocuments', { | ||
textDocuments | ||
}) | ||
workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the language server is on and I disable it in the settings, the server turns off without any need to reload. But if I try to turn it back on in settings, I need to reload the page to see the language server running again: Screen.Recording.2022-10-05.at.4.13.12.PM.movDo we want it to be able to turn it back on without needing to reload? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [I] this is a disposable and should be tracked. |
||
if ( | ||
event.affectsConfiguration(ConfigKey.DISABLE_LANGUAGE_SERVER) && | ||
this.isRunning() && | ||
this.languageServerDisabled() | ||
) { | ||
this.stop() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we dispose of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you! |
||
} | ||
}) | ||
} | ||
|
||
return this | ||
} | ||
|
@@ -68,6 +54,14 @@ export class LanguageClientWrapper extends Disposable { | |
this.client.stop() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Q] Are you sure that stopping the client stops the server? |
||
} | ||
|
||
private isRunning() { | ||
return this.client.isRunning() | ||
} | ||
|
||
private languageServerDisabled() { | ||
return getConfigValue<boolean>(ConfigKey.DISABLE_LANGUAGE_SERVER) | ||
} | ||
|
||
private getServerOptions(): ServerOptions { | ||
const debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] } | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,16 +10,14 @@ import { | |
Location, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. File definitions inside of Screen.Recording.2022-10-05.at.5.35.47.PM.mov |
||
Position, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What should be shown if a file hasn't been opened yet? Currently, it appears to take us somewhere that doesn't seem to make sense: Screen.Recording.2022-10-05.at.5.39.18.PM.mov |
||
Range, | ||
DocumentSymbol, | ||
TextDocumentItem | ||
DocumentSymbol | ||
} from 'vscode-languageserver/node' | ||
import { TextDocument } from 'vscode-languageserver-textdocument' | ||
import { URI } from 'vscode-uri' | ||
import { TextDocumentWrapper } from './TextDocumentWrapper' | ||
|
||
export class LanguageServer { | ||
private documentsKnownToEditor!: TextDocuments<TextDocument> | ||
private documentsFromDvcClient: TextDocumentWrapper[] = [] | ||
|
||
public listen(connection: _Connection) { | ||
this.documentsKnownToEditor = new TextDocuments(TextDocument) | ||
|
@@ -34,24 +32,6 @@ export class LanguageServer { | |
return this.onDefinition(params) | ||
}) | ||
|
||
connection.onRequest( | ||
'initialTextDocuments', | ||
(params: { textDocuments: TextDocumentItem[] }) => { | ||
this.documentsFromDvcClient = params.textDocuments.map( | ||
({ uri, languageId, version, text: content }) => { | ||
const textDocument = TextDocument.create( | ||
uri, | ||
languageId, | ||
version, | ||
content | ||
) | ||
|
||
return this.wrap(textDocument) | ||
} | ||
) | ||
} | ||
) | ||
|
||
this.documentsKnownToEditor.listen(connection) | ||
|
||
connection.listen() | ||
|
@@ -61,16 +41,6 @@ export class LanguageServer { | |
const openDocuments = this.documentsKnownToEditor.all() | ||
const acc: TextDocumentWrapper[] = openDocuments.map(doc => this.wrap(doc)) | ||
|
||
for (const textDocument of this.documentsFromDvcClient) { | ||
const userAlreadyOpenedIt = this.documentsKnownToEditor.get( | ||
textDocument.uri | ||
) | ||
|
||
if (!userAlreadyOpenedIt) { | ||
acc.push(textDocument) | ||
} | ||
} | ||
|
||
return acc | ||
} | ||
|
||
|
@@ -81,10 +51,7 @@ export class LanguageServer { | |
const doc = this.documentsKnownToEditor.get(uri) | ||
|
||
if (!doc) { | ||
const alternative = this.documentsFromDvcClient.find( | ||
txtDoc => txtDoc.uri === uri | ||
) | ||
return alternative ?? null | ||
return null | ||
} | ||
|
||
return this.wrap(doc) | ||
|
@@ -94,18 +61,33 @@ export class LanguageServer { | |
return new TextDocumentWrapper(doc) | ||
} | ||
|
||
private isPossibleFilePath(symbol: DocumentSymbol) { | ||
const isFile = symbol.kind === SymbolKind.File | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Q] any ideas how |
||
const isProperty = symbol.kind === SymbolKind.Property | ||
|
||
return isFile || (symbol.detail && isProperty) | ||
} | ||
|
||
private getFilePathFromSymbol(symbol: DocumentSymbol) { | ||
if (symbol.kind === SymbolKind.File) { | ||
return symbol.name | ||
} | ||
|
||
return symbol.detail ?? '' | ||
} | ||
|
||
private getFilePathLocations( | ||
symbolUnderCursor: DocumentSymbol, | ||
allDocs: TextDocumentWrapper[] | ||
) { | ||
if (symbolUnderCursor.kind !== SymbolKind.File) { | ||
if (!this.isPossibleFilePath(symbolUnderCursor)) { | ||
return [] | ||
} | ||
|
||
const filePath = symbolUnderCursor.name | ||
const filePath = this.getFilePathFromSymbol(symbolUnderCursor) | ||
|
||
const matchingFiles = allDocs.filter(doc => | ||
URI.file(doc.uri).fsPath.endsWith(filePath) | ||
URI.file(doc.uri).path.endsWith(filePath) | ||
) | ||
|
||
return matchingFiles.map(doc => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was very confused as to how the "ring-fenced" language server managed to bring down the extension host.
This would be what caused the crashes. A synchronous call to read every file would definitely bring down the thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The watcher would not have caused this issue. We already have at least one watcher which watches the entire workspace (and more).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more I think about this the more I feel like the approach was backwards. We should be parsing each
dvc.yaml
file when it is opened and checking to see which symbols are files. From there we read those files so that we can jump into them if needed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relevant: https://code.visualstudio.com/updates/v1_72#_improved-startup-performance
See "NO MORE SYNC FS CALLS"