Skip to content

Commit b3d80d8

Browse files
authored
feat: add jsDoc typecast completions, add tests (#180)
1 parent 44e45b1 commit b3d80d8

File tree

4 files changed

+62
-28
lines changed

4 files changed

+62
-28
lines changed

typescript/src/completions/asSuggestions.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { findChildContainingExactPosition } from '../utils'
2+
import { sharedCompletionContext } from './sharedContext'
3+
4+
const getCastedTypeCompletionEntry = (typeChecker: ts.TypeChecker, node: ts.Expression) => {
5+
const typeAtLocation = typeChecker.getTypeAtLocation(node)
6+
const type = typeChecker.typeToString(typeAtLocation)
7+
const widenType = typeChecker.typeToString(typeChecker.getBaseTypeOfLiteralType(typeAtLocation))
8+
9+
return {
10+
kind: ts.ScriptElementKind.unknown,
11+
name: type === widenType ? type : widenType,
12+
sortText: '!',
13+
}
14+
}
15+
16+
export default () => {
17+
const typeChecker = sharedCompletionContext.program.getTypeChecker()
18+
const { position, fullText, prior } = sharedCompletionContext
19+
20+
// as completions
21+
if (fullText.slice(0, position - 1).endsWith('as')) {
22+
const node = findChildContainingExactPosition(sharedCompletionContext.sourceFile, position - 2)
23+
if (!node || !ts.isAsExpression(node)) return
24+
const entry = getCastedTypeCompletionEntry(typeChecker, node.expression)
25+
prior.entries.push(entry)
26+
return
27+
}
28+
29+
// jsdoc typecast completions
30+
const node = findChildContainingExactPosition(sharedCompletionContext.sourceFile, position)
31+
if (!node) return
32+
let typeCastedNode: ts.ParenthesizedExpression | undefined
33+
node.forEachChild(node => {
34+
if (ts.isParenthesizedExpression(node) && ts.getJSDocTypeTag(node)) typeCastedNode = node
35+
})
36+
37+
if (!typeCastedNode) return
38+
const entry = getCastedTypeCompletionEntry(typeChecker, typeCastedNode.expression)
39+
prior.entries.push(entry)
40+
}

typescript/src/completionsAtPosition.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import stringTemplateTypeCompletions from './completions/stringTemplateType'
3131
import localityBonus from './completions/localityBonus'
3232
import functionCompletions from './completions/functionCompletions'
3333
import staticHintSuggestions from './completions/staticHintSuggestions'
34-
import asSuggestions from './completions/asSuggestions'
34+
import typecastCompletions from './completions/typecastCompletions'
3535

3636
export type PrevCompletionMap = Record<
3737
string,
@@ -300,7 +300,7 @@ export const getCompletionsAtPosition = (
300300
if (c('improveJsxCompletions') && leftNode) prior.entries = improveJsxCompletions(prior.entries, leftNode, position, sourceFile, c('jsxCompletionsMap'))
301301

302302
prior.entries = localityBonus(prior.entries) ?? prior.entries
303-
asSuggestions()
303+
typecastCompletions()
304304
prior.entries.push(...(staticHintSuggestions() ?? []))
305305

306306
const processedEntries = new Set<ts.CompletionEntry>()

typescript/test/completions.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,3 +860,23 @@ test('In Keyword Completions', () => {
860860
}
861861
`)
862862
})
863+
describe('Typecast completions', () => {
864+
test('As completions', () => {
865+
const [pos] = newFileContents(/*ts*/ `
866+
const b = 5
867+
const a = b as /*|*/
868+
`)
869+
const completions = getCompletionsAtPosition(pos!)?.entriesSorted
870+
871+
expect(completions?.[0]?.name).toEqual('number')
872+
})
873+
test('jsDoc typecast', () => {
874+
const [pos] = newFileContents(/*ts*/ `
875+
const b = 5
876+
const a = /** @type {/*|*/} */(b)
877+
`)
878+
const completions = getCompletionsAtPosition(pos!)?.entriesSorted
879+
880+
expect(completions?.[0]?.name).toEqual('number')
881+
})
882+
})

0 commit comments

Comments
 (0)