Skip to content

Commit 62b0822

Browse files
committed
feat: Add methodSnippetsInsertText setting so you can finally preview method snippets and use them in Vue files!
1 parent 845e5cc commit 62b0822

File tree

6 files changed

+92
-58
lines changed

6 files changed

+92
-58
lines changed

src/configurationType.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,15 @@ export type Configuration = {
358358
* @default true
359359
*/
360360
enableMethodSnippets: boolean
361+
/**
362+
* Wether add insert text and detail to every function completion on each suggest trigger (instead of expanding method snippet after completion accept).
363+
* This way you can enable support for method snippets in Vue files.
364+
* `methodSnippets.replaceArguments` isn't supported for now.
365+
* This is not enabled by default as it might be really slow in some cases.
366+
* Recommended to try!
367+
* @default disable
368+
*/
369+
methodSnippetsInsertText: 'disable' | 'only-local' | 'all'
361370
/**
362371
* ```ts
363372
* const example = ({ a }, b?, c = 5, ...d) => { }
@@ -540,6 +549,12 @@ export type Configuration = {
540549
* @default false
541550
*/
542551
'experiments.changeKindToFunction': boolean
552+
/**
553+
* Use workaround method for inserting name of TypeScript suggestion.
554+
* If you move to next suggestion and then to previous, and then run *insert name of completion* via keybinding, name of **last resolved** completion will be inserted, so you might prefer to enable this setting. Also it makes this feature work in Vue.
555+
* @default false
556+
*/
557+
'experiments.enableInsertNameOfSuggestionFix': boolean
543558
/**
544559
* Map *symbol - array of modules* to change sorting of imports - first available takes precedence in auto import code fixes (+ import all action)
545560
*
@@ -605,12 +620,6 @@ export type Configuration = {
605620
typeAlias: string
606621
interface: string
607622
}
608-
/**
609-
* Use workaround method for inserting name of TypeScript suggestion.
610-
* If you move to next suggestion and then to previous, and then run *insert name of completion* via keybinding, name of **last resolved** completion will be inserted, so you might prefer to enable this setting. Also it makes this feature work in Vue.
611-
* @default false
612-
*/
613-
'experimental.enableInsertNameOfSuggestionFix': boolean
614623
}
615624

616625
// scrapped using search editor. config: caseInsesetive, context lines: 0, regex: const fix\w+ = "[^ ]+"

typescript/src/completions/changeKindToFunction.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { oneOf } from '@zardoy/utils'
2+
import constructMethodSnippet from '../constructMethodSnippet'
3+
import { insertTextAfterEntry } from '../utils'
4+
import { sharedCompletionContext } from './sharedContext'
5+
6+
export default (entries: ts.CompletionEntry[]) => {
7+
const { languageService, c, sourceFile, position } = sharedCompletionContext
8+
9+
const methodSnippetInsertTextMode = c('methodSnippetsInsertText')
10+
const enableResolvingInsertText = c('enableMethodSnippets') && methodSnippetInsertTextMode !== 'disable'
11+
const changeKindToFunction = c('experiments.changeKindToFunction')
12+
13+
if (!enableResolvingInsertText && !changeKindToFunction) return
14+
15+
const typeChecker = languageService.getProgram()!.getTypeChecker()!
16+
// let timeSpend = 0
17+
const newEntries = entries.map(entry => {
18+
const patch = (): ts.CompletionEntry | undefined => {
19+
const { kind, symbol } = entry
20+
if (
21+
!enableResolvingInsertText &&
22+
!oneOf(
23+
kind,
24+
ts.ScriptElementKind.alias,
25+
ts.ScriptElementKind.memberVariableElement,
26+
ts.ScriptElementKind.variableElement,
27+
ts.ScriptElementKind.localVariableElement,
28+
ts.ScriptElementKind.constElement,
29+
ts.ScriptElementKind.variableElement,
30+
)
31+
) {
32+
return
33+
}
34+
if (methodSnippetInsertTextMode === 'only-local' && entry.source) return
35+
if (!symbol) return
36+
const { valueDeclaration } = symbol
37+
if (!valueDeclaration) return
38+
39+
// const dateNow = Date.now()
40+
if (enableResolvingInsertText) {
41+
const resolveData = {} as { isAmbiguous: boolean }
42+
const methodSnippet = constructMethodSnippet(languageService, sourceFile, position, symbol, c, resolveData)
43+
if (!methodSnippet || resolveData.isAmbiguous) return
44+
return {
45+
...entry,
46+
insertText: insertTextAfterEntry(entry, `(${methodSnippet.map((x, i) => `$\{${i + 1}:${x}}`).join(', ')})`),
47+
labelDetails: {
48+
detail: `(${methodSnippet.join(', ')})`,
49+
description: ts.displayPartsToString(entry.sourceDisplay),
50+
},
51+
kind: changeKindToFunction ? ts.ScriptElementKind.functionElement : entry.kind,
52+
isSnippet: true,
53+
}
54+
}
55+
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, valueDeclaration)
56+
const signatures = typeChecker.getSignaturesOfType(type, ts.SignatureKind.Call)
57+
// timeSpend += Date.now() - dateNow
58+
if (signatures.length === 0) return
59+
60+
return { ...entry, kind: ts.ScriptElementKind.functionElement }
61+
}
62+
63+
return patch() ?? entry
64+
})
65+
66+
// remove logging once stable
67+
// console.log('changeKindToFunction time:', timeSpend)
68+
69+
return newEntries
70+
}

typescript/src/completions/objectLiteralCompletions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getFullTypeChecker, isTs5 } from '../utils'
1+
import { getFullTypeChecker, insertTextAfterEntry, isTs5 } from '../utils'
22
import { sharedCompletionContext } from './sharedContext'
33

44
export default (prior: ts.CompletionInfo): ts.CompletionEntry[] | void => {
@@ -66,7 +66,7 @@ export default (prior: ts.CompletionInfo): ts.CompletionEntry[] | void => {
6666
const insertSnippetVariant = completingStyleMap.find(([, detector]) => detector(type!, typeChecker))?.[0] ?? fallbackSnippet
6767
if (!insertSnippetVariant) continue
6868
const [insertSnippetText, insertSnippetPreview] = typeof insertSnippetVariant === 'function' ? insertSnippetVariant() : insertSnippetVariant
69-
const insertText = entry.name.replace(/\$/g, '\\$') + insertSnippetText
69+
const insertText = insertTextAfterEntry(entry, insertSnippetText)
7070
const index = entries.indexOf(entry)
7171
entries.splice(index + (keepOriginal === 'before' ? 1 : 0), keepOriginal === 'remove' ? 1 : 0, {
7272
...entry,

typescript/src/completionsAtPosition.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ import adjustAutoImports from './completions/adjustAutoImports'
2626
import addSourceDefinition from './completions/addSourceDefinition'
2727
import { sharedCompletionContext } from './completions/sharedContext'
2828
import displayImportedInfo from './completions/displayImportedInfo'
29-
import changeKindToFunction from './completions/changeKindToFunction'
3029
import functionPropsAndMethods from './completions/functionPropsAndMethods'
3130
import { getTupleSignature } from './tupleSignature'
3231
import stringTemplateTypeCompletions from './completions/stringTemplateType'
3332
import localityBonus from './completions/localityBonus'
33+
import functionCompletions from './completions/functionCompletions'
3434

3535
export type PrevCompletionMap = Record<
3636
string,
@@ -357,9 +357,7 @@ export const getCompletionsAtPosition = (
357357
if (exactNode) {
358358
prior.entries = filterJsxElements(prior.entries, exactNode, position, languageService, c) ?? prior.entries
359359
}
360-
if (c('experiments.changeKindToFunction')) {
361-
prior.entries = changeKindToFunction(prior.entries)
362-
}
360+
prior.entries = functionCompletions(prior.entries) ?? prior.entries
363361

364362
if (c('correctSorting.enable')) {
365363
prior.entries = prior.entries.map(({ ...entry }, index) => ({

typescript/src/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,6 @@ export const patchMethod = <T, K extends keyof T>(obj: T, method: K, overriden:
281281
})
282282
}
283283
}
284+
285+
export const insertTextAfterEntry = (entryOrName: ts.CompletionEntry | string, appendText: string) =>
286+
(typeof entryOrName === 'string' ? entryOrName : entryOrName.name).replace(/\$/g, '\\$') + appendText

0 commit comments

Comments
 (0)