Skip to content

Commit 0ae5b20

Browse files
authored
Merge pull request #207 from zardoy/develop
2 parents aba470c + 23cf01a commit 0ae5b20

10 files changed

+4567
-3449
lines changed

pnpm-lock.yaml

+4,460-3,423
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { matchParents } from '../../utils'
2+
import { CodeAction } from '../getCodeActions'
3+
4+
export default {
5+
id: 'fixOppositeTagName',
6+
kind: 'quickfix',
7+
name: 'Fix opposite tag name',
8+
tryToApply(sourceFile, position, range, node) {
9+
const elem = matchParents(node, ['Identifier', 'JsxOpeningElement']) ?? matchParents(node, ['Identifier', 'JsxClosingElement'])
10+
if (!elem) return
11+
const tagNamesDiffers = elem.parent.openingElement.tagName.getText() !== elem.parent.closingElement.tagName.getText()
12+
if (tagNamesDiffers) {
13+
const isCurrentlyAtOpening = elem.parent.openingElement === elem
14+
const oppositeElem = isCurrentlyAtOpening ? elem.parent.closingElement.tagName : elem.parent.openingElement.tagName
15+
return [
16+
{
17+
start: oppositeElem.getStart(),
18+
length: oppositeElem.getWidth(),
19+
newText: elem.tagName.getText(),
20+
},
21+
]
22+
}
23+
return
24+
},
25+
} satisfies CodeAction

typescript/src/codeActions/extended/declareMissingProperties.ts

-13
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,3 @@ export default {
8383
return
8484
},
8585
} as ExtendedCodeAction
86-
87-
const testCode = () => {
88-
const tester = (code: string) => {
89-
// ^ - problem location in which quickfix needs to be tested (applied)
90-
// | - cursor position after quickfix is applied
91-
// [[...]] - applied part of the code
92-
/* TODO */
93-
}
94-
95-
tester(/* ts */ `
96-
const b = ({ b, ^a }: { b[[, a/*|*/]] }) => {}
97-
`)
98-
}

typescript/src/codeActions/getCodeActions.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import declareMissingProperties from './extended/declareMissingProperties'
1010
import { renameParameterToNameFromType, renameAllParametersToNameFromType } from './custom/renameParameterToNameFromType'
1111
import addDestructure_1 from './custom/addDestructure/addDestructure'
1212
import fromDestructure_1 from './custom/fromDestructure/fromDestructure'
13+
import fixClosingTagName from './custom/fixClosingTagName'
1314

1415
const codeActions: CodeAction[] = [
1516
addDestructure_1,
@@ -19,6 +20,7 @@ const codeActions: CodeAction[] = [
1920
splitDeclarationAndInitialization,
2021
renameParameterToNameFromType,
2122
renameAllParametersToNameFromType,
23+
fixClosingTagName,
2224
]
2325
const extendedCodeActions: ExtendedCodeAction[] = [declareMissingProperties]
2426

typescript/src/completions/filterJsxComponents.ts

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ export default (entries: ts.CompletionEntry[], node: ts.Node, position: number,
1414
) {
1515
return
1616
}
17+
// const startLineNode = ts.getLineAndCharacterOfPosition(node.getSourceFile(), node.pos).line
18+
// const startLinePosition = ts.getLineAndCharacterOfPosition(node.getSourceFile(), position).line
19+
// if (startLineNode !== startLinePosition) return
20+
const identifier = ts.isJsxSelfClosingElement(node) || ts.isJsxOpeningElement(node) ? node.tagName : null
21+
// if already got name and we are not typing it
22+
if (identifier && identifier.end < position) return
1723

1824
const nodeText = node.getText().slice(0, position - (node.pos + node.getLeadingTriviaWidth()))
1925
// workaround for <div test |></div>

typescript/src/completions/fixPropertiesSorting.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default (entries: ts.CompletionEntry[]) => {
3131
const typeChecker = program.getTypeChecker()
3232
let sourceProps: string[]
3333
if (isJsxElem) {
34-
const type = typeChecker.getContextualType((node as ts.JsxOpeningElement).attributes)
34+
const type = typeChecker.getContextualType((targetNode as ts.JsxOpeningElement).attributes)
3535
if (!type) return
3636
// usually component own props defined first like interface Props extends ... {} or type A = Props & ..., but this is not a case with mui...
3737
sourceProps = (type.isIntersection() ? type.types.flatMap(type => type.getProperties()) : type.getProperties()).map(symbol => symbol.name)

typescript/src/completions/jsxAttributes.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default (
1111
sourceFile: ts.SourceFile,
1212
jsxCompletionsMap: Configuration['jsxCompletionsMap'],
1313
): ts.CompletionEntry[] => {
14+
const originalNode = node
1415
// ++ patch with jsxCompletionsMap
1516
// -- don't
1617
// <div| - identifier, not attribute --
@@ -78,13 +79,24 @@ export default (
7879
const enableJsxAttributesShortcuts = sharedCompletionContext.c('jsxAttributeShortcutCompletions.enable')
7980
if (enableJsxAttributesShortcuts !== 'disable') {
8081
const locals = collectLocalSymbols(node, sharedCompletionContext.typeChecker)
82+
let attrib = originalNode.parent as ts.JsxAttribute | undefined
83+
if (!ts.isJsxAttribute(attrib!)) {
84+
attrib = undefined
85+
}
8186
entries = entries.flatMap(entry => {
8287
if (locals.includes(entry.name)) {
8388
const insertText = `${entry.name}={${entry.name}}`
84-
const additionalSuggestions = {
89+
const pos = attrib ? attrib.end - attrib.getWidth() : 0
90+
const additionalSuggestions: ts.CompletionEntry = {
8591
...entry,
8692
name: insertText,
8793
insertText,
94+
replacementSpan: attrib
95+
? {
96+
start: pos,
97+
length: attrib.end - pos,
98+
}
99+
: undefined,
88100
}
89101
return enableJsxAttributesShortcuts === 'after' ? [entry, additionalSuggestions] : [additionalSuggestions, entry]
90102
}

typescript/src/decorateLinkedEditing.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@ export default (proxy: ts.LanguageService, languageService: ts.LanguageService,
2323
lastLinkedEditingRangeRequest.fileName === fileName
2424
) {
2525
lastLinkedEditingRangeRequest.pos = position
26-
lastLinkedEditingRangeRequest.result.ranges[0]!.length++
26+
const startRange = lastLinkedEditingRangeRequest.result.ranges[0]!
27+
const endRange = lastLinkedEditingRangeRequest.result.ranges[1]!
28+
startRange.length++
2729
lastLinkedEditingRangeRequest.result.ranges[1]!.start++
30+
2831
lastLinkedEditingRangeRequest.result.ranges[1]!.length++
29-
return lastLinkedEditingRangeRequest.result
32+
const leadingText = fileContent.slice(startRange.start, startRange.start + startRange.length)
33+
const endingText = fileContent.slice(endRange.start, endRange.start + endRange.length)
34+
if (leadingText === endingText) {
35+
return lastLinkedEditingRangeRequest.result
36+
}
3037
}
3138
lastLinkedEditingRangeRequest = undefined
3239

typescript/test/completions.spec.ts

+50-8
Original file line numberDiff line numberDiff line change
@@ -527,21 +527,38 @@ test('Fix properties sorting', () => {
527527
a.b({/*2*/})./*3*/
528528
}
529529
530-
declare function MyComponent(props: { b?; c? } & { a? }): JSX.Element
531-
<MyComponent /*4*/ />;
532-
533530
let a: { b:{}, a() } = {
534531
/*5*/
535532
}
533+
534+
declare function MyComponent(props: { b?; c? } & { a? }): JSX.Element
535+
<MyComponent /*4*/ />;
536+
<MyComponent
537+
c=''
538+
/*41*/
539+
/>;
540+
<MyComponent
541+
test2=''
542+
/*41*/
543+
test=''
544+
/>;
545+
<MyComponent /*42*/
546+
test2=''
547+
/>;
536548
`)
537549
const assertSorted = (marker: number, expected: string[]) => {
538550
const { entriesSorted } = getCompletionsAtPosition(currentTestingContext.markers[marker]!)!
539-
expect(entriesSorted.map(x => x.name)).toEqual(expected)
551+
expect(
552+
entriesSorted.map(x => x.name),
553+
`${marker}`,
554+
).toEqual(expected)
540555
}
541556
assertSorted(1, ['c', 'b'])
542557
assertSorted(2, ['c', 'b'])
543558
assertSorted(3, ['c', 'b'])
544559
assertSorted(4, ['b', 'c', 'a'])
560+
assertSorted(41, ['b', 'c', 'a'])
561+
assertSorted(42, ['b', 'c', 'a'])
545562
assertSorted(5, ['b', 'b', 'a', 'a'])
546563
settingsOverride.fixSuggestionsSorting = false
547564
})
@@ -570,11 +587,36 @@ testTs5('Change to function kind', () => {
570587
})
571588

572589
testTs5('Filter JSX Components', () => {
573-
const tester = fourslashLikeTester(/* ts */ `
574-
const a = () => {}
575-
a/*1*/
590+
overrideSettings({
591+
// improveJsxCompletions: false,
592+
'experiments.excludeNonJsxCompletions': true,
593+
})
594+
const tester = fourslashLikeTester(/* tsx */ `
595+
const someFunction = () => {}
596+
declare namespace JSX {
597+
interface IntrinsicElements {
598+
superSpan: any;
599+
}
600+
}
601+
// return < // TODO
602+
return <s/*1*/
603+
`)
604+
tester.completion(1, {
605+
excludes: ['someFunction'],
606+
includes: {
607+
names: ['superSpan'],
608+
},
609+
})
610+
// https://github.com/zardoy/typescript-vscode-plugins/issues/205
611+
const tester2 = fourslashLikeTester(/* tsx */ `
612+
const Img = ({ alt }) => {}
613+
<Img\n\t/*1*/\n/>
576614
`)
577-
// TODO
615+
tester2.completion(1, {
616+
includes: {
617+
names: ['alt'],
618+
},
619+
})
578620
})
579621

580622
test('Omit<..., ""> suggestions', () => {

typescript/test/testing.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ export const fourslashLikeTester = (contents: string, fileName = entrypoint, { d
178178
)!
179179
const newContentsActual = tsFull.textChanges.applyChanges(getCurrentFile(), edits[0]!.textChanges)
180180
if (newContent) {
181-
expect(dedentString(newContent), `at marker ${mark}`).toEqual(newContentsActual)
181+
expect(newContentsActual, `at marker ${mark}`).toEqual(dedentString(newContent))
182182
}
183183
return newContentsActual
184184
}

0 commit comments

Comments
 (0)