typescript-language-server / typescript-language-server

TypeScript & JavaScript Language Server

Home Page:https://www.npmjs.com/package/typescript-language-server

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Use diagnostic range when requesting `getCodeFixes`

bradymadden97 opened this issue · comments

I noticed a discrepancy between vscode (tsserver directly) and typescript-language-server.

Given the files:

/// file:App.tsx
import React from "react";

export function App() {
  return (
    <div>
      <Link />
    </div>
  );
}
/// file:Link.tsx
import React from "react";

export function Link() {
  return <div />;
}

We will receive a diagnostic over Link in App.tsx:
image

When placing the cursor after the k in Link in App.tsx, vscode makes a getCodeFixes request that appears as follows:

Info 653  [11:43:26.587] request:
    {
      "seq": 146,
      "type": "request",
      "command": "getCodeFixes",
      "arguments": {
        "file": "/Users/brady/Desktop/test/App.tsx",
        "startLine": 7,
        "startOffset": 8,
        "endLine": 7,
        "endOffset": 12,
        "errorCodes": [
          2304
        ]
      }
    }
Info 654  [11:43:26.587] forEachExternalModuleToImportFrom autoImportProvider: 0.13391613960266113
Perf 655  [11:43:26.588] 146::getCodeFixes: elapsed time (in milliseconds) 1.2297
Info 656  [11:43:26.588] response:
    {"seq":0,"type":"response","command":"getCodeFixes","request_seq":146,"success":true,"body":[{"fixName":"import","description":"Add import from \"./Link\"","changes":[{"fileName":"/Users/brady/Desktop/test/App.tsx","textChanges":[{"start":{"line":3,"offset":1},"end":{"line":3,"offset":1},"newText":"import { Link } from \"./Link\";\n"}]}]}]}

As you can see, the requested start and end positions are the range of the diagnostic:

        "startLine": 7,
        "startOffset": 8,
        "endLine": 7,
        "endOffset": 12,

And as such, a code fix to import Link is returned from tsserver.
This is because vscode's implementation uses the diagnostic's range as the range: https://github.com/microsoft/vscode/blob/fd4b93963e6456d09175410ea1cc84cb796ad241/extensions/typescript-language-features/src/languageFeatures/quickFix.ts#L330

Compare this with typescript-language-server, which requests the following:

Info 92   [15:48:08.612] request:
    {"seq":12,"type":"request","command":"getCodeFixes","arguments":{"file":"/home/runner/HardtofindScratchyEngineers/src/App.tsx","startLine":7,"startOffset":12,"endLine":7,"endOffset":12,"errorCodes":[2304]}}
Perf 93   [15:48:08.613] 12::getCodeFixes: elapsed time (in milliseconds) 1.4205
Info 94   [15:48:08.613] response:
    {"seq":0,"type":"response","command":"getCodeFixes","request_seq":12,"success":true,"body":[]}

As you can see the requested start and end positions are simply from the cursor position and thus tsserver returns no results:

        "startLine": 7,
        "startOffset": 12,
        "endLine": 7,
        "endOffset": 12,

Now, it could be argued that this bug is instead on tsserver to include the trailing position in getCodeFixes range, such that a request from 7:12-7:12 would still receive the fix. But I think this could be an opportunity to update typescript-language-server to adjust its request range instead.

I don't think this is something the client can handle - as if the client sent the range of the diagnostic as its range, it would be misrepresenting the current editor selection, and potentially receive other invalid code actions.

Thanks for your consideration!

I would say that this IS the responsibility of the client, though I can't see it stated explicitly in the LSP specification. But this is how it works in VSCode's client (it's not the typescript-language-features' code that computes the range, it's the generic VSCode code that does it) and I know other LSP clients that follow that behavior (Sublime Text LSP, for example).

Clients can handle it by expanding the range to account for diagnostics that overlap the current point (or range).

Wait, the link I sent above is from typescript-language-features, not vscode core: https://github.com/microsoft/vscode/blob/fd4b93963e6456d09175410ea1cc84cb796ad241/extensions/typescript-language-features/src/languageFeatures/quickFix.ts#L330

That's exactly why I thought this should be a typescript-language-server responsibility.

If generic clients adjust their range like this for all servers, they're going to end up pulling in additional code actions that don't actually make sense for the user's current selection (an example being tsserver returning "surround" code actions when you have a selection > 0 width.

EDIT: I see where sublime adjusts the range now. However, I'm still curious about your thoughts on the vscode behavior. https://github.com/sublimelsp/LSP/blob/d4538fa34ff689672ec60ab8f585b3cb4c828f49/plugin/documents.py#L278

Fwiw, I don't see Sublime doing this in their core code either https://github.com/sublimelsp/LSP/blob/d4538fa34ff689672ec60ab8f585b3cb4c828f49/plugin/core/views.py#L412

Just to be clear, I'm not talking about passing along the diagnostics as context - I already do that. I'm talking about adjusting the range sent to tsserver to match vscode's behavior, such that tsserver returns quickfixes relevant to that diagnostic range.

Hmm, you might be right that this should be the responsibility of this server.

But also VSCode's implementation requests code actions separately for each passed diagnostic rather than bundling them all in a single request like typescript-language-server. So that probably also would need to get adjusted (assuming that it has some benefits).