Skip to content

CompletionItem.additionalTextEdits can violate the LSP spec #964

@savetheclocktower

Description

@savetheclocktower

In the LSP spec, additionalTextEdits on a CompletionItem are described as follows:

/**
 * An optional array of additional text edits that are applied when
 * selecting this completion. Edits must not overlap (including the same
 * insert position) with the main edit nor with themselves.
 *
 * Additional text edits should be used to change text unrelated to the
 * current cursor position (for example adding an import statement at the
 * top of the file if the completion item will insert an unqualified type).
 */

Yet I've encountered at least one scenario in which two text edits want to insert at the same position.

Imagine this setup:

// ./src/foo.ts

export function avarice() {
  // TODO
}

export function brillig () {
  // TODO
}

export function brilliant () {
  // TODO
}
// ./index.ts
import {
  avarice
} from './src/foo'

function main () {
  avarice()
  // Type `bril` here and wait for a completion menu to appear.
}

This scenario is designed to invoke asResolvedCompletionItem so that additionalTextEdits get populated on a CompletionItem. Once I do this, I see two edits that ought to be a single TextEdit:

{
  "label": "brillig",
  "kind": 3,
  // …
  "detail": "Auto import from './src/foo'\nfunction brillig(): void",
  "additionalTextEdits": [
    {
      "range": {
        "start": { "line": 1, "character": 9 },
        "end": { "line": 1, "character": 9 }
      },
      "newText": ","
    },
    {
      "range": {
        "start": { "line": 1, "character": 9 },
        "end": { "line": 1, "character": 9 }
      },
      "newText": "\n  brillig"
    }
  ]
}

Since both of these edits want to insert at the same position, the only thing for a consumer of this CompletionItem to do is try to interpret the meaning. In this case, it's safe to assume that the two edits can be merged — but not in the general case.

I'm not familiar enough with the underlying logic that leads these two additionalTextEdits to be separate instead of combined, but it seems like the obvious solution here is to combine them on the server side before resolving the CompletionItem.

This is occurring in Pulsar using pulsar-ide-typescript, but I can't identify any part of this that could be explained by incorrect behavior on the part of the language client.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy