Skip to content

Commit 52d73ff

Browse files
authored
Merge pull request #60 from zardoy/develop
2 parents 554a3cb + 0da7a28 commit 52d73ff

18 files changed

+1690
-97
lines changed

buildTsPlugin.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const result = await buildTsPlugin('typescript', undefined, undefined, {
66
minify: !process.argv.includes('--watch'),
77
metafile: true,
88
banner: {
9-
js: 'let ts',
9+
js: 'let ts, tsFull;',
1010
// js: 'const log = (...args) => console.log(...args.map(a => JSON.stringify(a)))',
1111
},
1212
})

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
"type-fest": "^2.13.1",
6363
"typed-jsonfile": "^0.2.1",
6464
"typescript": "^4.8.3",
65-
"typescript-old": "npm:[email protected]",
6665
"vitest": "^0.15.1",
6766
"vscode-manifest": "^0.0.4"
6867
},
@@ -75,7 +74,7 @@
7574
"@vscode/emmet-helper": "^2.8.4",
7675
"@vscode/test-electron": "^2.1.5",
7776
"@zardoy/utils": "^0.0.9",
78-
"@zardoy/vscode-utils": "^0.0.41",
77+
"@zardoy/vscode-utils": "^0.0.45",
7978
"chai": "^4.3.6",
8079
"chokidar": "^3.5.3",
8180
"chokidar-cli": "^3.0.0",
@@ -93,6 +92,8 @@
9392
"rambda": "^7.2.1",
9493
"require-from-string": "^2.0.2",
9594
"string-dedent": "^3.0.1",
95+
"ts-expose-internals": "^4.8.4",
96+
"unleashed-typescript": "^1.3.0",
9697
"vscode-framework": "^0.0.18",
9798
"vscode-uri": "^3.0.6"
9899
},

patches/ts-expose-internals+4.8.4.patch

Lines changed: 1452 additions & 0 deletions
Large diffs are not rendered by default.

pnpm-lock.yaml

Lines changed: 24 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/extension.ts

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/* eslint-disable @typescript-eslint/no-require-imports */
22
import * as vscode from 'vscode'
33
import { defaultJsSupersetLangs } from '@zardoy/vscode-utils/build/langs'
4-
import { getActiveRegularEditor } from '@zardoy/vscode-utils'
54
import { extensionCtx, getExtensionSettingId } from 'vscode-framework'
65
import { pickObj } from '@zardoy/utils'
76
import { Configuration } from './configurationType'
@@ -12,6 +11,7 @@ import experimentalPostfixes from './experimentalPostfixes'
1211
import migrateSettings from './migrateSettings'
1312
import figIntegration from './figIntegration'
1413
import apiCommands from './apiCommands'
14+
import onCompletionAccepted from './onCompletionAccepted'
1515

1616
export const activateTsPlugin = (tsApi: { configurePlugin; onCompletionAccepted }) => {
1717
let webWaitingForConfigSync = false
@@ -46,38 +46,7 @@ export const activateTsPlugin = (tsApi: { configurePlugin; onCompletionAccepted
4646
})
4747
syncConfig()
4848

49-
tsApi.onCompletionAccepted((item: vscode.CompletionItem & { document: vscode.TextDocument }) => {
50-
const enableMethodSnippets = vscode.workspace.getConfiguration(process.env.IDS_PREFIX, item.document).get('enableMethodSnippets')
51-
const { documentation = '' } = item
52-
const documentationString = documentation instanceof vscode.MarkdownString ? documentation.value : documentation
53-
const insertFuncArgs = /<!-- insert-func: (.*)-->/.exec(documentationString)?.[1]
54-
console.debug('insertFuncArgs', insertFuncArgs)
55-
if (enableMethodSnippets && insertFuncArgs !== undefined) {
56-
const editor = getActiveRegularEditor()!
57-
const startPos = editor.selection.start
58-
const nextSymbol = editor.document.getText(new vscode.Range(startPos, startPos.translate(0, 1)))
59-
if (!['(', '.'].includes(nextSymbol)) {
60-
const snippet = new vscode.SnippetString('')
61-
snippet.appendText('(')
62-
const args = insertFuncArgs.split(',')
63-
for (let [i, arg] of args.entries()) {
64-
if (!arg) continue
65-
// skip empty, but add tabstops if we explicitly want it!
66-
if (arg === ' ') arg = ''
67-
snippet.appendPlaceholder(arg)
68-
if (i !== args.length - 1) snippet.appendText(', ')
69-
}
70-
71-
snippet.appendText(')')
72-
void editor.insertSnippet(snippet, undefined, {
73-
undoStopAfter: false,
74-
undoStopBefore: false,
75-
})
76-
if (vscode.workspace.getConfiguration('editor.parameterHints').get('enabled'))
77-
void vscode.commands.executeCommand('editor.action.triggerParameterHints')
78-
}
79-
}
80-
})
49+
onCompletionAccepted(tsApi)
8150

8251
if (process.env.PLATFORM === 'web') {
8352
const possiblySyncConfig = async () => {

src/onCompletionAccepted.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import * as vscode from 'vscode'
2+
import { getActiveRegularEditor } from '@zardoy/vscode-utils'
3+
import { watchExtensionSettings } from '@zardoy/vscode-utils/build/settings'
4+
import { getExtensionSetting, Settings } from 'vscode-framework'
5+
import { oneOf } from '@zardoy/utils'
6+
7+
export default (tsApi: { onCompletionAccepted }) => {
8+
let justAcceptedReturnKeywordSuggestion = false
9+
10+
tsApi.onCompletionAccepted((item: vscode.CompletionItem & { document: vscode.TextDocument }) => {
11+
const enableMethodSnippets = vscode.workspace.getConfiguration(process.env.IDS_PREFIX, item.document).get('enableMethodSnippets')
12+
const { insertText, documentation = '', kind } = item
13+
if (kind === vscode.CompletionItemKind.Keyword && insertText === 'return ') {
14+
justAcceptedReturnKeywordSuggestion = true
15+
}
16+
17+
const documentationString = documentation instanceof vscode.MarkdownString ? documentation.value : documentation
18+
const insertFuncArgs = /<!-- insert-func: (.*)-->/.exec(documentationString)?.[1]
19+
console.debug('insertFuncArgs', insertFuncArgs)
20+
if (enableMethodSnippets && insertFuncArgs !== undefined) {
21+
const editor = getActiveRegularEditor()!
22+
const startPos = editor.selection.start
23+
const nextSymbol = editor.document.getText(new vscode.Range(startPos, startPos.translate(0, 1)))
24+
if (!['(', '.'].includes(nextSymbol)) {
25+
const snippet = new vscode.SnippetString('')
26+
snippet.appendText('(')
27+
const args = insertFuncArgs.split(',')
28+
for (let [i, arg] of args.entries()) {
29+
if (!arg) continue
30+
// skip empty, but add tabstops if we explicitly want it!
31+
if (arg === ' ') arg = ''
32+
snippet.appendPlaceholder(arg)
33+
if (i !== args.length - 1) snippet.appendText(', ')
34+
}
35+
36+
snippet.appendText(')')
37+
void editor.insertSnippet(snippet, undefined, {
38+
undoStopAfter: false,
39+
undoStopBefore: false,
40+
})
41+
if (vscode.workspace.getConfiguration('editor.parameterHints').get('enabled')) {
42+
void vscode.commands.executeCommand('editor.action.triggerParameterHints')
43+
}
44+
}
45+
}
46+
})
47+
48+
conditionallyRegister(
49+
'suggestions.keywordsInsertText',
50+
() =>
51+
vscode.workspace.onDidChangeTextDocument(({ document, contentChanges, reason }) => {
52+
if (!justAcceptedReturnKeywordSuggestion) return
53+
if (document !== vscode.window.activeTextEditor?.document) return
54+
try {
55+
if (oneOf(reason, vscode.TextDocumentChangeReason.Redo, vscode.TextDocumentChangeReason.Undo)) {
56+
return
57+
}
58+
59+
const char = contentChanges[0]?.text
60+
if (char?.length !== 1 || contentChanges.some(({ text }) => text !== char)) {
61+
return
62+
}
63+
64+
if (char === ';') {
65+
void vscode.window.activeTextEditor.edit(builder => {
66+
for (const { range } of contentChanges) {
67+
const pos = range.start
68+
builder.delete(new vscode.Range(pos.translate(0, -1), pos))
69+
}
70+
})
71+
}
72+
} finally {
73+
justAcceptedReturnKeywordSuggestion = false
74+
}
75+
}),
76+
val => val !== 'none',
77+
)
78+
}
79+
80+
const conditionallyRegister = <T extends keyof Settings>(
81+
settingKey: T,
82+
registerFn: () => vscode.Disposable,
83+
acceptSettingValue: (val: Settings[T]) => boolean = val => !!val,
84+
) => {
85+
let disposable: vscode.Disposable | undefined
86+
const changeRegisterState = () => {
87+
const registerState = acceptSettingValue(getExtensionSetting(settingKey))
88+
if (registerState) {
89+
if (!disposable) disposable = registerFn()
90+
} else {
91+
disposable?.dispose()
92+
disposable = undefined
93+
}
94+
}
95+
96+
changeRegisterState()
97+
watchExtensionSettings([settingKey], changeRegisterState)
98+
}

typescript/src/codeActions/toggleBraces.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const tryToApply: ApplyCodeAction = (ts, sourceFile, pos, range) => {
66
const currentNode = findChildContainingPosition(ts, sourceFile, pos)
77
if (!currentNode) return
88
const closestBlock = findClosestParent(
9-
ts,
109
currentNode,
1110
[
1211
ts.SyntaxKind.IfStatement,

typescript/src/completions/arrayMethods.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,30 @@ export default (entries: ts.CompletionEntry[], position: number, sourceFile: ts.
3939
inferredName = defaultItemName
4040
}
4141

42+
// workaround for
43+
// https://github.com/microsoft/vscode/blob/4765b898acb38a44f9dd8fa7ed48e833fff6ecc6/extensions/typescript-language-features/src/languageFeatures/completions.ts#L99
44+
// (overriding default range)
45+
// after []. .fill was appearing above .filter becuase .filter is snippet in insertText, not changing insertText of .fill so vscode method completions calls work as expected
46+
const resetRangeKinds = fullText.slice(position - 1, position) === '.' && [
47+
ts.ScriptElementKind.constElement,
48+
ts.ScriptElementKind.memberFunctionElement,
49+
ts.ScriptElementKind.memberVariableElement,
50+
]
51+
52+
const arrayItemSnippet = c('arrayMethodsSnippets.addArgTabStop') ? `(\${2:${inferredName}})` : inferredName
53+
let insertInnerSnippet = `${arrayItemSnippet} => $3`
54+
if (c('arrayMethodsSnippets.addOuterTabStop')) insertInnerSnippet = `\${1:${insertInnerSnippet}}`
55+
4256
return entries.map(entry => {
43-
if (!arrayMethodsToPatch.includes(entry.name.replace(/^ /, ''))) return entry
44-
const arrayItemSnippet = c('arrayMethodsSnippets.addArgTabStop') ? `(\${2:${inferredName}})` : inferredName
45-
let insertInnerSnippet = `${arrayItemSnippet} => $3`
46-
if (c('arrayMethodsSnippets.addOuterTabStop')) insertInnerSnippet = `\${1:${insertInnerSnippet}}`
57+
if (!arrayMethodsToPatch.includes(entry.name.replace(/^ /, ''))) {
58+
if (resetRangeKinds && resetRangeKinds.includes(entry.kind) && !entry.replacementSpan) {
59+
return {
60+
...entry,
61+
replacementSpan: { start: position, length: 0 },
62+
}
63+
}
64+
return entry
65+
}
4766
return {
4867
...entry,
4968
insertText: `${entry.insertText ?? entry.name}(${insertInnerSnippet})`,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
export default (entries: ts.CompletionEntry[], scriptSnapshot: ts.IScriptSnapshot, position: number, node) => {
2+
const charAhead = scriptSnapshot.getText(position, position + 1)
3+
if (charAhead === ' ') return entries
4+
const bannedKeywords = [
5+
'true',
6+
'false',
7+
'undefined',
8+
'null',
9+
'never',
10+
'unknown',
11+
'any',
12+
'symbol',
13+
'string',
14+
'number',
15+
'boolean',
16+
'object',
17+
'this',
18+
'catch',
19+
'constructor',
20+
'continue',
21+
'break',
22+
'debugger',
23+
'default',
24+
'super',
25+
'import',
26+
]
27+
const bannedKeywordsWhenInType = ['const', 'void', 'import']
28+
const inType = isTypeNode(node)
29+
return entries.map(entry => {
30+
if (entry.kind !== ts.ScriptElementKind.keyword || bannedKeywords.includes(entry.name) || (inType && bannedKeywordsWhenInType.includes(entry.name)))
31+
return entry
32+
return { ...entry, insertText: `${entry.name} ` }
33+
})
34+
}
35+
36+
const isTypeNode = (node: ts.Node) => {
37+
if (ts.isTypeNode(node)) {
38+
// built-in types
39+
return true
40+
}
41+
42+
if (inTypeReference(node)) return true
43+
44+
return false
45+
46+
function inTypeReference(node: ts.Node) {
47+
if (ts.isTypeReferenceNode(node)) {
48+
return true
49+
}
50+
51+
return node.parent && inTypeReference(node.parent)
52+
}
53+
}

0 commit comments

Comments
 (0)