Skip to content

Expose completion entry symbol for plugins API #51936

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

Closed
5 tasks done
zardoy opened this issue Dec 17, 2022 · 2 comments · Fixed by #52560
Closed
5 tasks done

Expose completion entry symbol for plugins API #51936

zardoy opened this issue Dec 17, 2022 · 2 comments · Fixed by #52560
Labels
API Relates to the public API for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@zardoy
Copy link
Contributor

zardoy commented Dec 17, 2022

Suggestion

🔍 Search Terms

languageService.getCompletionEntrySymbol

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Hi! I don't have very deep knowledge of TS internals, but it seems very obvious that completion entries are coming from symbols and getting that symbol information per entry is not possible in any way (calling getCompletionEntrySymbol for every entry seems to be inefficient inside of getCompletionsAtPosition as it calles it).

📃 Motivating Example and 💻 Use Cases

plugin

Probably absolutely any plugin that wants to improve or change existing completions lists, so I can imagine hundreds of hundreds use cases. But I have a few real world cases that I really need sometimes. For example removing marking (or even removing some) global symbol completions:

const isLibCompletion = (symbol: ts.Symbol) => {
    const fileName = symbol.declarations?.[0]?.getSourceFile().fileName
    if (!fileName) return
    if (!fileName.includes('node_modules/typescript/lib/')) return
    return basename(fileName).slice(0, -'.d.ts'.length)
}

let prior = languageService.getCompletionsAtPosition(fileName, position, options)
if (!prior.isGlobalCompletion) return
entries.map(entry => {
    if (entry.sourceDisplay) return entry
    const symbol = entry['symbol'] as ts.Symbol | undefined
    if (!symbol) return entry
    const libCompletionEnding = isLibCompletion(symbol)
    if (!libCompletionEnding) return entry
    return {
        ...entry,
        labelDetails: {
            ...entry.labelDetails,
            description: libCompletionEnding,
        },
    }
})

With this patch I can get it (in my case it just adds symbol in %LOCALAPPDATA%\Programs\Microsoft VS Code\resources\app\extensions\node_modules\typescript\lib\tsserver.js at L135650):

before (lib.dom suggestions) after
Screenshot 2022-12-17 at 21 41 49 Screenshot 2022-12-17 at 21 42 03

Another (but more advanced) cases would be:

  1. Adding call signature (like objectLiteralMethodSnippets but for global / property access completions)
  2. Filter only jsx components completions in <Foo[||]>
  3. Change insertText of object literal property completion depending on JSDoc tags (for example @default)

... also changing sorting, insertText or even removing completions depending on contextual or declaration type or fileName (location) of where symbol is declared.

There are no workarounds known for me! (except of manually getting container and symbols of it), but it might become out of sync if manually maintained in plugin.

@andrewbranch andrewbranch added Suggestion An idea for TypeScript API Relates to the public API for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Dec 19, 2022
@zardoy
Copy link
Contributor Author

zardoy commented Dec 22, 2022

Hey, @andrewbranch, don't want to be annoying, but I wonder what kind of feedback is it awaiting? If there is anything else I should provide / do / discuss here, please let me know when you have time.

Others want features I can build using this information and I eagarly want to implement it, so again if there is any chance I can contribute to this let me also know about this. I'm also not sure of how it well it suits in the API, having it next to entry completion is just super handy (even ideal) for API usage (if we compare to getCompletionEntrySymbol arguments, which receives only name and source). Maybe it would be okay to have other getCompletionEntrySymbol method that would receive whole completionEntry as argument instead?

@andrewbranch
Copy link
Member

getCompletionEntrySymbol exists because it’s expected to get called against a different program than the one that gave the completion; e.g., the user started typing, got a list of completions, and then typed a few more characters before needing to get additional details about the symbol. When more characters are typed, the previous program gets thrown away, and much of the work done to find that original symbol has to be redone. If the symbol was merged or otherwise transient, it won’t exist anymore; a new one with a new identity will. That’s why it appears to be so inefficient. It’s not designed to do what you want at all. It’s reconstructing the world because it’s supposed to be called when the world has been thrown away.

Just returning the symbol on the completion entry as you did in your patch is a better solution. It would have to be enabled by a parameter passed to getCompletionsAtPosition to make sure that TS Server never sees it. I don’t think I have an issue with this but I’d want to run it by others on the team after the holidays. Normally I wouldn’t suggest making a PR before something is approved, but since it’s a small change, that’s probably the best way for us not to forget about it. (Most of the team is already on vacation, and I’ll be gone for a couple weeks starting tomorrow.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API Relates to the public API for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants