Skip to content
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

Closed
wants to merge 10 commits into from

Conversation

wolmir
Copy link
Contributor

@wolmir wolmir commented Sep 29, 2022

This PR will allow the Goto Definition for feature for properties inside parameter files, and also for files referenced in the dvc.yaml.

It will also introduce an extension setting to disable the language server when needed.

Demo:

Screen.Recording.2022-10-04.at.14.15.38.mov

@wolmir wolmir force-pushed the dvc-lsp-definition-improvements branch from 5072476 to acdb4fe Compare October 3, 2022 12:26
@wolmir wolmir force-pushed the dvc-lsp-definition-improvements branch from d962189 to ca424a1 Compare October 4, 2022 14:02
@wolmir wolmir changed the title [WIP] Dvc lsp definition improvements LSP: Disable server settings and Better Goto Definition for property and file paths Oct 4, 2022
@wolmir wolmir changed the title LSP: Disable server settings and Better Goto Definition for property and file paths LSP: New settings option to Disable server and Better Goto Definition for property and file paths Oct 4, 2022
@wolmir wolmir changed the title LSP: New settings option to Disable server and Better Goto Definition for property and file paths LSP: New settings option to Disable server and Better Goto Definition for property and file paths (1/2) Oct 4, 2022
@codeclimate
Copy link

codeclimate bot commented Oct 4, 2022

Code Climate has analyzed commit 92e616f and detected 0 issues on this pull request.

The test coverage on the diff in this pull request is 88.4% (85% is the threshold).

This pull request will bring the total coverage in the repository to 96.6% (-0.2% change).

View more on Code Climate.

@wolmir wolmir marked this pull request as ready for review October 4, 2022 18:03
@mattseddon
Copy link
Member

Needs #2530 to be ready before merging.

this.isRunning() &&
this.languageServerDisabled()
) {
this.stop()
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we dispose of the onDidChangeConfiguration here? We could get rid of the this.isRunning() && this.languageServerDisabled() as well as this would only run once (when disabling the server the first time).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you!


private findLocationsForPropertySymbol(symbol: DocumentSymbol) {
const propertyPath = symbol.detail
const itIsHere = propertyPath && this.hasProperty(propertyPath)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick, but is it possible to rename isHere as it follows our boolean naming conventions more.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, thanks!

visit
} from 'yaml'
import { BaseLanguageHelper } from './baseLanguageHelper'
import * as RegExes from './regexes'
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you can still import the regexes individually (ex.: filePaths), I think it'd be better to rename the exported constants in regexes.ts to identify them as regexes and simply import them individually. The other solution would be to wrap them all in a regexes object and export that instead, but I think the first solution is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's what I tried first, but I worried if anyone would be annoyed about typing a prefix every time they create/use the constant.
But now that I'm writing this I realised that typing Regexes.something is exactly the same thing and now I'm feeling really dumb.
I'll change it, thanks!

}

protected toJSON(): unknown {
return this.rootNode.toJS()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why toJS() instead of toJSON()? Might be worth a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes more sense, thanks!

@@ -1,3 +1,4 @@
export const variableTemplates = /\${([^}]+)}/g
export const filePaths = /[\d/A-Za-z]+\.[A-Za-z]+/g
export const alphadecimalWords = /[\dA-Za-z]+/g
export const propertyPathLike = /[\d.A-Z[\]a-z]+/g
Copy link
Contributor

Choose a reason for hiding this comment

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

Any differences between this file and languageServer/src/languageHelpers/regexes.ts? They should be merged if so. Either way we should only have one file only for regexes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Forgot about it, will delete it 👍

const textDocuments = files.map(filePath => {
const uri = Uri.file(filePath).toString()
const languageId = filePath.endsWith('yaml') ? 'yaml' : 'json'
const text = readFileSync(filePath, 'utf8')
Copy link
Member

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.

Copy link
Member

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

Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

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

await this.client.sendRequest('initialTextDocuments', {
textDocuments
})
workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => {
Copy link
Contributor

Choose a reason for hiding this comment

The 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.mov

Do we want it to be able to turn it back on without needing to reload?

import * as RegExes from './regexes'

export class YamlHelper extends BaseLanguageHelper<Document> {
protected rootNode!: Document
Copy link
Contributor

Choose a reason for hiding this comment

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

Never seen a ! used like this before! Learned something new today :)

@@ -10,16 +10,14 @@ import {
Location,
Copy link
Contributor

Choose a reason for hiding this comment

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

File definitions inside of cmds don't seem to work correctly on my end:

Screen.Recording.2022-10-05.at.5.35.47.PM.mov

@@ -10,16 +10,14 @@ import {
Location,
Position,
Copy link
Contributor

Choose a reason for hiding this comment

The 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

await this.client.sendRequest('initialTextDocuments', {
textDocuments
})
workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => {
Copy link
Member

Choose a reason for hiding this comment

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

[I] this is a disposable and should be tracked.

@@ -68,6 +54,14 @@ export class LanguageClientWrapper extends Disposable {
this.client.stop()
Copy link
Member

Choose a reason for hiding this comment

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

[Q] Are you sure that stopping the client stops the server?

@@ -0,0 +1,105 @@
import { has } from 'lodash'
Copy link
Member

Choose a reason for hiding this comment

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

Should be using lodash subpackages for consistency with the rest of the app.

@@ -94,18 +61,33 @@ export class LanguageServer {
return new TextDocumentWrapper(doc)
}

private isPossibleFilePath(symbol: DocumentSymbol) {
const isFile = symbol.kind === SymbolKind.File
Copy link
Member

Choose a reason for hiding this comment

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

[Q] any ideas how symbol.kind is set to SymbolKind.File?

findLocationsFor(symbol: DocumentSymbol): Location[]
}

export abstract class BaseLanguageHelper<RootNode> implements LanguageHelper {
Copy link
Member

Choose a reason for hiding this comment

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

It's usual practice in the codebase to have this a <T>. I was looking for where <RootNode> is defined for a couple of minutes.

SymbolKind
} from 'vscode-languageserver/node'

export interface LanguageHelper {
Copy link
Member

Choose a reason for hiding this comment

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

[N] Probably don't need this interface on top of the abstract class.

const textDocuments = files.map(filePath => {
const uri = Uri.file(filePath).toString()
const languageId = filePath.endsWith('yaml') ? 'yaml' : 'json'
const text = readFileSync(filePath, 'utf8')
Copy link
Member

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.

@@ -45,132 +33,11 @@ export class TextDocumentWrapper implements ITextDocumentWrapper {
return parseDocument(this.getText())
Copy link
Member

Choose a reason for hiding this comment

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

getYamlDocument is no longer used anywhere (seems like it has been moved into the languageHelper) it should be removed from this class and the interface.

@mattseddon mattseddon marked this pull request as draft January 30, 2023 04:11
@mattseddon mattseddon closed this Jan 30, 2023
@mattseddon mattseddon deleted the dvc-lsp-definition-improvements branch January 30, 2023 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants