-
Notifications
You must be signed in to change notification settings - Fork 36
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
Wrong hyperlinked element (definition, documentLink) when there are no Psi #98
Comments
I was just about to comment on #644 that it's almost certainly due to the lack of a |
A bit more on this for whoever ends up fielding this in the future (potentially myself). Contrary to my previous comment here, it looks like the right fix for this would be to register a I've tried to see if there might be some way to adjust the highlighted range without having to introduce more discrete target elements, but unfortunately I haven't been able to find anything. A little experimentation in the past past with a I've considered the use of |
If I understand correctly your comment, using If it that, an idea is to implement If we can do that it would be fantastic but we should improve again to use the LSP location source range.
|
I would like to avoid consuming this lsp request since the symbol response size can be big and for large files, this method should ve avoid to be consumed. |
Sorry, let me elaborate a bit more. First, regarding:
it's not about Ctrl/Cmd+Click but rather Ctrl+Cmd+Mouseover, so it's not going to result in a concrete action firing, at least not an And regarding this:
including /** Doc comment. */
export class Foo {
// Line comment
static bar() {
console.log('Hello, world.');
}
} it would yield a PSI "tree" like: /** Doc comment. */
export class <class:"Foo"> {
// Line comment
<method:"bar">() {
<variable:"console">.<member:"log">('Hello, world.');
}
} With the information we have about the language grammar from the file type and/or client config -- specifically brace/bracket/paren pairs, string literal bounds characters, comment prefixes/suffixes, etc. -- we could go even further and yield the following confidently: <blockComment:"/** Doc comment. */">
<token:"export"><psiWhitespace><token:"class"><psiWhitespace><class:"Foo"><psiWhitespace><lbrace:"{">
<psiWhitespace>
<lineComment:"// Line comment">
<method:"bar"><lbrace:"("><rbrace:")"><psiWhitespace><lbrace:"{">
<psiWhitespace>
<variable:"console"><dot:"."><member:"log"><lbrace:"("><stringLiteral:"'Hello, world.'"><rbrace:")"><terminator:";">
<psiWhitespace>
<rbrace:"}">
<psiWhitespace>
<rbrace:"}"> That's already extremely useful and should result in a If we found that it was possible to use <blockComment:"/** Doc comment. */">
<declaration:"Foo">
<token:"export"><psiWhitespace><token:"class"><psiWhitespace>
<nameIdentifier:"Foo">
<class:"Foo">
</nameIdentifier:"Foo">
<psiWhitespace><lbrace:"{">
<psiWhitespace>
<lineComment:"// Line comment">
<declaration:"bar">
<nameIdentifier:"bar">
<method:"bar">
</nameIdentifier:"bar">
<lbrace:"("><rbrace:")"><psiWhitespace><lbrace:"{">
<psiWhitespace>
<variable:"console"><dot:"."><member:"log"><lbrace:"("><stringLiteral:"'Hello, world.'"><rbrace:")"><terminator:";">
<psiWhitespace>
<rbrace:"}">
<declaration:"bar">
<psiWhitespace>
<rbrace:"}">
</declaration:"Foo"> I'll look into prototyping the first and second phases of this as explained above that only rely on the semantic tokens that we should already have at any given time. Hopefully that might happen this coming week, but I have two pending releases of my own plugin right now, one for Salesforce Spring '25 and one for SLDS Validator LSP integration. Once I get past both of those, I'll take a look at this. |
Please keep in mind that documentSymbol can be really expensive and very too big for large file. For instance vscode uses documentSymbol to fill outline. In vscode-xml we had to limit the symbols number on server side to avoid freezing vscode. The problem was not about the vscode outline but about the deserialzation of the json. The compute of the documentSymbol can be slow according some language server and cannot return the full symbols that we could expect. |
Concrete implementation thoughtsIt looks like the set of pseudo-PSI elements that can/should be derived from semantic tokens can be based on the combination of
They are considered declarations if found with any of the following
and are otherwise considered references/usages. Additionally the
While these PSI elements would be quite coarse-grained categorically, they could include the full original token type/modifier information for any consumers that might need them. ExamplesHere are a few concrete examples from specific language servers: TypeScriptThe following TypeScript file: /** Doc comment. */
export class Foo {
// Line comment
static bar() {
console.log('Hello, world.');
}
} results in the following semantic token types/modifiers: /** Doc comment. */
export class <class:declaration>Foo</class:declaration> {
// Line comment
static <member:declaration.static>bar</member:declaration.static>() {
<variable:defaultLibrary>console</variable:defaultLibrary>.<member:defaultLibrary>log</member:defaultLibrary>('Hello, world.');
}
} and would result in the following PSI elements (only including those directly derived from semantic tokens): /** Doc comment. */
export class <declaration:"Foo> {
// Line comment
static <declaration:"bar">() {
<reference:"console">.<reference:"log">('Hello, world.');
}
} GoHere's another example using Go showing other token types/modifiers including first-class tokens for comments and string literals: package main
import "fmt"
/** Block comment */
func main() {
// Line comment
fmt.Println("hello world")
} which yields the following semantic tokens: <keyword:>package</keyword:> <namespace:>main</namespace:>
<keyword:>import</keyword:> "<namespace:>fmt</namespace:>"
<comment:>/** Block comment */</comment:>
<keyword:>func</keyword:> <function:definition>main</function:definition>() {
<comment:>// Line comment</comment:>
<namespace:>fmt</namespace:>.<function:>Println</function:>(<string:>"hello world"</string:>)
} and would therefore have the following PSI elements (again, including only those derived from semantic tokens here): <keyword:"package"> <namespace:"main">
<keyword:"import"> "<namespace:"fmt">"
<comment:"/** Block comment */">
<keyword:"func"> <declaration:"main">() {
<comment:"// Line comment">
<reference:"fmt">.<reference:"Println">(<stringLiteral:"\"hello world\"">)
} JavaAnd another example using Java via jdtls: /*
* File header comment.
*/
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* Class comment.
*/
public class Hello {
/**
* Method; comment.
* @param args command-line arguments
*/
public static void main(String[] args) {
List<String> strings = new ArrayList<>(args);
}
} which yields the following semantic token types/modifiers: /*
* File header comment.
*/
import <namespace:importDeclaration>java</namespace:importDeclaration>.<namespace:importDeclaration>util</namespace:importDeclaration>.<class:public.generic.importDeclaration>ArrayList</class:public.generic.importDeclaration>;
import <namespace:importDeclaration>java</namespace:importDeclaration>.<namespace:importDeclaration>util</namespace:importDeclaration>.<class:public.generic.importDeclaration>LinkedHashSet</class:public.generic.importDeclaration>;
import <namespace:importDeclaration>java</namespace:importDeclaration>.<namespace:importDeclaration>util</namespace:importDeclaration>.<interface:public.generic.importDeclaration>List</interface:public.generic.importDeclaration>;
import <namespace:importDeclaration>java</namespace:importDeclaration>.<namespace:importDeclaration>util</namespace:importDeclaration>.<interface:public.generic.importDeclaration>Set</interface:public.generic.importDeclaration>;
/**
* Class comment.
*/
<modifier:>public</modifier:> <modifier:>class</modifier:> <class:declaration.public>Hello</class:declaration.public> {
/**
* Method; comment.
* <keyword:documentation>@param</keyword:documentation> <parameter:documentation>args</parameter:documentation> command-line arguments
*/
<modifier:>public</modifier:> <modifier:>static</modifier:> void <method:static.declaration.public>main</method:static.declaration.public>(<class:readonly.public>String</class:readonly.public>[] <parameter:declaration>args</parameter:declaration>) {
<interface:public.generic>List</interface:public.generic><<class:readonly.public.typeArgument>String</class:readonly.public.typeArgument>> <variable:declaration>strings</variable:declaration> = new ArrayList<>(<parameter:>args</parameter:>);
}
} and the following PSI elements: /*
* File header comment.
*/
import <reference:"java">.<reference:"util">.<reference:"ArrayList">;
import <reference:"java">.<reference:"util">.<reference:"LinkedHashSet">;
import <reference:"java">.<reference:"util">.<reference:"List">;
import <reference:"java">.<reference:"util">.<reference:"Set">;
/**
* Class comment.
*/
<modifier:"public"> <modifier:"class"> <declaration:"Hello"> {
/**
* Method; comment.
* <keyword:"@param"> <reference:"args"> command-line arguments
*/
<modifier:"public"> <modifier:"static"> void <declaration:"main">(<class:"String">[] <declaration:"args">) {
<reference:"List"><<reference:"String">> <declaration:"strings"> = new ArrayList<>(<reference:"args">);
}
} |
Turns out that approach works pretty well! Here's a demo showing it in TypeScript, Java, and Go where I'm holding down Ctrl (on Windows) the whole time I'm mousing around these documents: This is only using semantic tokens to create a sparsely-populated PSI "tree" for the file. It doesn't yet solve the issue with Go To Declaration or Usages on a declaration showing usages, but I'm pretty sure that should be pretty simple to implement with these changes. I have no idea yet what breaks due to these changes, but I'll try to circle back to this branch later in the coming week and see if I can polish it up for a PR. |
Okay, I was able to work on this quite a bit more today, and I have it pretty much working across the board. Here's a demo: As the demo hopefully conveys, the following all work properly now:
Unfortunately this can only work for TextMate files and not plain text files associated with abstract file types. This is because the This branch is in pretty solid shape, but I do need to add some class/method comments describing the changes, and I need to write some tests to exercise it (and fix any existing tests that may be broken by these changes). I'll see if I can get a PR going over the next few days. |
Hey @angelozerr, Do you have try this idea ? |
No I give up this idea to work on more important features. If you want to try it, yiu are welcome! But please keep in sync with @SCWells72 who has some ideas to fix this issue |
Please see this comment for a small demo showing the fix for this issue and the one with Go To Declaration or Usages that I'll hopefully be running through the PR process later this week. |
Great! |
There's not a PR yet, but hopefully I'll open one in the next day or two. The short version is that it adds a |
Thank you for your explanations :) |
I repeat me but we need to be carefull with this solution. We must never have some freeze or some memory problem with large file. It is my fear. |
Yes, I've taken note of your concern, but as an alternative I'm thinking of creating a |
Hopefully when you see the actual changes, that concern will be put to rest. This all happens in response to the existing semantic tokens logic. No new LSP calls have been added for this. And if there are no semantic tokens -- either because they haven't been received yet or because the language server doesn't support semantic tokens -- my changes still fix the issue with the entire file being hyperlinked on Ctrl/Cmd+Mouse hover. |
PR for this is now open as #853. |
When a file is not parsed with a specific Psi parser (TEXT, texmate), the hyperlinked element takes the full content of the file.
Here is a demo with the ugly problem:
To manage LSP definition we implement GotoDeclarationHandler which doesn't provide the capability to customize the source range.
I have tried to support LSP definition with other means and one idea was to implement https://github.com/JetBrains/intellij-community/blob/6c42d0fad1db66faa4e5549935ca3eab91c5b2ca/platform/core-api/src/com/intellij/model/psi/ImplicitReferenceProvider.java#L32 where we can customize the source range.
The only problem is that this method is called every time (when you are typing when the file is loaded, etc). I discovered with #356 that we can track which action is executed.
One idea is:
The text was updated successfully, but these errors were encountered: