Skip to content

Pass textDocument/definition requests to TSLS if ALS found nothing #59888

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

Open
Markiewic opened this issue Feb 7, 2025 · 5 comments
Open

Pass textDocument/definition requests to TSLS if ALS found nothing #59888

Markiewic opened this issue Feb 7, 2025 · 5 comments
Labels
area: language-service Issues related to Angular's VS Code language service
Milestone

Comments

@Markiewic
Copy link

Markiewic commented Feb 7, 2025

Which @angular/* package(s) are relevant/related to the feature request?

language-service

Description

TL;DR: If ALS returns null to a textDocument/definition request in a TS file, then pass the request to TSLS. This does not currently happen, and because of this, Zed editor, which uses a language server for the entire file, cannot go the definition from a reference outside the "Angular-specific code".


We are developing an Angular extension for the Zed editor. Zed supports language servers and LSP. As I work, I compare the work of the extension on Zed and VS Code.

Currently, the Angular VS Code extension passes the textDocument/references commands from the entire .ts file to ALS, and ALS responds to them correctly, taking into account that symbol can also be used in the template. The handler of this method does not have the angularOnly condition, unlike the others:

function getReferencesAtPosition(
fileName: string,
position: number,
): ts.ReferenceEntry[] | undefined {
return ngLS.getReferencesAtPosition(fileName, position);
}

Perhaps because TS is used there unconditionally.

The situation is different with the textDocument/definition command: the Angular extension passes the command to ALS only under certain conditions. It seems to be decided here. ALS also has the angularOnly condition, which prevents the command from being passed to TSLS.

if (angularOnly || !isTypeScriptFile(fileName)) {
return ngLS.getDefinitionAndBoundSpan(fileName, position);
} else {
// If TS could answer the query, then return that result. Otherwise, return from Angular LS.
return (
tsLS.getDefinitionAndBoundSpan(fileName, position) ??
ngLS.getDefinitionAndBoundSpan(fileName, position)
);
}

The catch is that in Zed the selected language server works on the entire file, and all commands are passed to it unconditionally. Including the textDocument/definition command. If you request a definition in a TS file outside the "Angular-specific code", ALS cannot handle this command because angularOnly: true is set and passes null, although it could pass the command to TSLS and return the correct answer (nathansbradshaw/zed-angular#19 (comment)).

I understand that the angularOnly condition in the handlers did not just appear out of nowhere. But for what reason was this condition bypassed in the textDocument/references handler? Isn't this reason also suitable for getting rid of the angularOnly condition for the textDocument/definition command? I don't think this will harm ALS in any way. But if you make it possible to pass the command to TSLS, Zed will be able to receive the correct definition and the feature will work as expected.

Proposed solution

I can propose a simple and stupid solution there. Just update function getDefinitionAndBoundSpan so that it falls back to TSLS if ALS returns null.

  function getDefinitionAndBoundSpan(
    fileName: string,
    position: number,
  ): ts.DefinitionInfoAndBoundSpan | undefined {
    if (!isTypeScriptFile(fileName)) {
      return ngLS.getDefinitionAndBoundSpan(fileName, position);
    } else {
      return (
        ngLS.getDefinitionAndBoundSpan(fileName, position) ??
        tsLS.getDefinitionAndBoundSpan(fileName, position)
      );
    }
  }

At least it works. Although, from what I understand, tsLS probably shouldn't be used at all if angularOnly is true. However, I can go deeper and provide a more valid solution if I'm sure my request is valid.

Alternatives considered

To tell the truth, the feature in Zed is crooked, but it works: since the definition request always returns null, Zed thinks that any symbol is a definition and immediately requests and displays references, among which there is also a definition, but you have to search it in the list.

It is also possible to replace the angularOnly parameter in the downloaded package with false, but in this case, when you hover over the symbols, two hints are displayed simultaneously: from TSLS and ALS. In addition, making changes to the package is not the best practice.

@Markiewic Markiewic changed the title PasstextDocument/definition requests to TSLS if ALS found nothing Pass textDocument/definition requests to TSLS if ALS found nothing Feb 7, 2025
@alxhub alxhub added the area: language-service Issues related to Angular's VS Code language service label Feb 7, 2025
@ngbot ngbot bot added this to the needsTriage milestone Feb 7, 2025
@jpike88
Copy link

jpike88 commented Mar 25, 2025

This sounds potentially nuts... but... what if we just, while downloading the latest angular LSP, did a regex replace of the downloaded payload, and patch it on the fly?

@jpike88
Copy link

jpike88 commented Mar 26, 2025

This sounds potentially nuts... but... what if we just, while downloading the latest angular LSP, did a regex replace of the downloaded payload, and patch it on the fly?

@Markiewic

I tried the above... but for it to work correctly, 'angular' LSP had to be the first in order... but it then bumps up against a Zed limitation, which is that non-angular projects would get caught up in it too and "go to definition" would fail for them.

I think that this is an issue that needs to be resolved on Zed's side, or in what ways they allow extensions to be developed. Ideally, the angular LSP should be skipped on a TypeScript file if there's no angular.json associated in that path, etc...

@atscott
Copy link
Contributor

atscott commented Mar 27, 2025

Am I understanding correctly that Zed does not allow two language servers to co-exist and provide functionality to the same file? If so, that feels like a much more fundamental problem than just updating how definitions are provided. We rely on typescript providing its own information where appropriate, as well as the built in language features for HTML to supplement the angular-only information in external template files.

@jpike88
Copy link

jpike88 commented Mar 28, 2025

@atscott correct this is a zed issue being tracked here and getting attention now

zed-industries/zed#24100

@atscott
Copy link
Contributor

atscott commented Mar 28, 2025

@jpike88 got it. That’s an interesting discussion. The priority for which language server to use for renaming is an unsolved problem in VSCode microsoft/vscode#115354. This is the reason we entirely disable rename functionality in TS files for VSCode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: language-service Issues related to Angular's VS Code language service
Projects
None yet
Development

No branches or pull requests

4 participants