diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 2b6b93dffcdeb..5459c1b495a9e 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3576,6 +3576,73 @@ export class TestState { } } + public baselineLinkedEditing(): void { + const baselineFile = this.getBaselineFileNameForContainingTestFile(".linkedEditing.txt"); + const files = this.testData.files; + + let baselineContent = ""; + let offset = 0; + for (const f of files) { + const result = getLinkedEditingBaselineWorker(f, offset, this.languageService); + baselineContent += result.baselineContent + `\n\n\n`; + offset = result.offset; + } + + Harness.Baseline.runBaseline(baselineFile, baselineContent); + + function getLinkedEditingBaselineWorker(activeFile: FourSlashFile, offset: number, languageService: ts.LanguageService) { + const fileName = activeFile.fileName; + let baselineContent = `=== ${fileName} ===\n`; + + // get linkedEdit at every position in the file, then group positions by their linkedEdit + const linkedEditsInFile = new Map(); + for(let pos = 0; pos < activeFile.content.length; pos++) { + const linkedEditAtPosition = languageService.getLinkedEditingRangeAtPosition(fileName, pos); + if (!linkedEditAtPosition) continue; + + const linkedEditString = JSON.stringify(linkedEditAtPosition); + const existingPositions = linkedEditsInFile.get(linkedEditString) ?? []; + linkedEditsInFile.set(linkedEditString, [...existingPositions, pos]); + } + + const linkedEditsByRange = [...linkedEditsInFile.entries()].sort((a, b) => a[1][0] - b[1][0]); + if (linkedEditsByRange.length === 0) { + return { baselineContent: baselineContent + activeFile.content + `\n\n--No linked edits found--`, offset }; + } + + let inlineLinkedEditBaselines: { start: number, end: number, index: number }[] = []; + let linkedEditInfoBaseline = ""; + for (const edit of linkedEditsByRange) { + const [linkedEdit, positions] = edit; + let rangeStart = 0; + for (let j = 0; j < positions.length - 1; j++) { + // for each distinct range in the list of positions, add an entry to the list of places that need to be annotated in the baseline + if (positions[j] + 1 !== positions[j + 1]) { + inlineLinkedEditBaselines.push({ start: positions[rangeStart], end: positions[j], index: offset }); + rangeStart = j + 1; + } + } + inlineLinkedEditBaselines.push({ start: positions[rangeStart], end: positions[positions.length - 1], index: offset }); + + // add the LinkedEditInfo with its index to the baseline + linkedEditInfoBaseline += `\n\n=== ${offset} ===\n` + linkedEdit; + offset++; + } + + inlineLinkedEditBaselines = inlineLinkedEditBaselines.sort((a, b) => a.start - b.start); + const fileText = activeFile.content; + baselineContent += fileText.slice(0, inlineLinkedEditBaselines[0].start); + for (let i = 0; i < inlineLinkedEditBaselines.length; i++) { + const e = inlineLinkedEditBaselines[i]; + const sliceEnd = inlineLinkedEditBaselines[i + 1]?.start; + baselineContent += `[|/*${e.index}*/` + fileText.slice(e.start, e.end) + `|]` + fileText.slice(e.end, sliceEnd); + } + + baselineContent += linkedEditInfoBaseline; + return { baselineContent, offset }; + } + } + public verifyMatchingBracePosition(bracePosition: number, expectedMatchPosition: number) { const actual = this.languageService.getBraceMatchingAtPosition(this.activeFile.fileName, bracePosition); diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index ae83c034f9ff8..5e22e2cffc780 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -181,6 +181,10 @@ export class VerifyNegatable { this.state.verifyLinkedEditingRange(map); } + public baselineLinkedEditing(): void { + this.state.baselineLinkedEditing(); + } + public isInCommentAtPosition(onlyMultiLineDiverges?: boolean) { this.state.verifySpanOfEnclosingComment(this.negative, onlyMultiLineDiverges); } diff --git a/tests/baselines/reference/linkedEditingJsxTag10.linkedEditing.txt b/tests/baselines/reference/linkedEditingJsxTag10.linkedEditing.txt new file mode 100644 index 0000000000000..d8322f53ea447 --- /dev/null +++ b/tests/baselines/reference/linkedEditingJsxTag10.linkedEditing.txt @@ -0,0 +1,97 @@ +=== /jsx0.tsx === +const jsx = <> + +--No linked edits found-- + + +=== /jsx1.tsx === +const jsx = + +--No linked edits found-- + + +=== /jsx2.tsx === +const jsx =
+ +--No linked edits found-- + + +=== /jsx3.tsx === +const jsx =
+ +--No linked edits found-- + + +=== /jsx4.tsx === +const jsx =
; + +--No linked edits found-- + + +=== /jsx5.tsx === +const jsx = <>
; + +--No linked edits found-- + + +=== /jsx6.tsx === +const jsx = div> ; + +--No linked edits found-- + + +=== /jsx7.tsx === +const jsx =
/div>; + +--No linked edits found-- + + +=== /jsx8.tsx === +const jsx =
; + +--No linked edits found-- + + +=== /jsx9.tsx === +const jsx = <[|/*0*/div|]> ; + +--No linked edits found-- + + +=== /jsx12.tsx === +const jsx = > ; + +--No linked edits found-- + + +=== /jsx13.tsx === +const jsx = <> />; + +--No linked edits found-- + + +=== /jsx14.tsx === +const jsx = <>
; + +--No linked edits found-- + + +=== /jsx15.tsx === +const jsx =
<>
; + +--No linked edits found-- + + diff --git a/tests/baselines/reference/linkedEditingJsxTag11.linkedEditing.txt b/tests/baselines/reference/linkedEditingJsxTag11.linkedEditing.txt new file mode 100644 index 0000000000000..c6000d3eb5443 --- /dev/null +++ b/tests/baselines/reference/linkedEditingJsxTag11.linkedEditing.txt @@ -0,0 +1,27 @@ +=== /customElements.tsx === +const jsx = <[|/*0*/fbt:enum|] knownProp="accepted" + unknownProp="rejected"> +; + +const customElement = <[|/*1*/custom-element|]>; + +const standardElement = + <[|/*2*/Link|] href="/hello" passHref> + <[|/*3*/Button|] component="a"> + Next + + ; + +=== 0 === +{"ranges":[{"start":13,"length":8},{"start":73,"length":8}],"wordPattern":"[a-zA-Z0-9:\\-\\._$]*"} + +=== 1 === +{"ranges":[{"start":108,"length":14},{"start":125,"length":14}],"wordPattern":"[a-zA-Z0-9:\\-\\._$]*"} + +=== 2 === +{"ranges":[{"start":172,"length":4},{"start":269,"length":4}],"wordPattern":"[a-zA-Z0-9:\\-\\._$]*"} + +=== 3 === +{"ranges":[{"start":209,"length":6},{"start":256,"length":6}],"wordPattern":"[a-zA-Z0-9:\\-\\._$]*"} + + diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index e948c8c5b129c..691605c814173 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -263,6 +263,7 @@ declare namespace FourSlashInterface { isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; linkedEditing(map: { [markerName: string]: LinkedEditingInfo | undefined }): void; + baselineLinkedEditing(): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; codeFix(options: { description: string | [string, ...(string | number)[]] | DiagnosticIgnoredInterpolations, diff --git a/tests/cases/fourslash/linkedEditingJsxTag10.ts b/tests/cases/fourslash/linkedEditingJsxTag10.ts index 509f858010e7e..eef7488aac8b2 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag10.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag10.ts @@ -48,43 +48,5 @@ // @Filename: /jsx15.tsx ////const jsx = ; -const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; -const linkedCursors9 = { - ranges: [{ start: test.markerByName("9").position, length: 3 }, { start: test.markerByName("9a").position, length: 3 }], - wordPattern, -}; - -verify.linkedEditing( { - "0": undefined, - "1": undefined, - "2": undefined, - "3": undefined, - "4": undefined, - "4a": undefined, - "5": undefined, - "5a": undefined, - "6": undefined, - "6a": undefined, - "7": undefined, - "7a": undefined, - "8": undefined, - "8a": undefined, - "9": linkedCursors9, - "9a": linkedCursors9, - "10": undefined, - "10a": undefined, - "11": undefined, - "11a": undefined, - "12": undefined, - "12a": undefined, - "13": undefined, - "13a": undefined, - "14": undefined, - "14a": undefined, - "14b": undefined, - "14c": undefined, - "15": undefined, - "15a": undefined, - "15b": undefined, - "15c": undefined, -}); \ No newline at end of file +verify.baselineLinkedEditing(); + diff --git a/tests/cases/fourslash/linkedEditingJsxTag11.ts b/tests/cases/fourslash/linkedEditingJsxTag11.ts index 3755f78a8cb0b..8e4fa5353746a 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag11.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag11.ts @@ -1,61 +1,19 @@ /// // for readability -////const jsx = ( -////
-////

-//// -////

-////
-////); // @Filename: /customElements.tsx -//// const jsx = -//// ; +//// ; //// -//// const customElement = ; +//// const customElement = ; //// //// const standardElement = -//// -//// +//// +//// +//// ; -const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; - -const linkedCursors1 = { - ranges: [{ start: test.markerByName("1").position, length: 8 }, { start: test.markerByName("4").position - 1, length: 8 }], - wordPattern, -}; -const linkedCursors2 = { - ranges: [{ start: test.markerByName("6").position, length: 14 }, { start: test.markerByName("9").position - 6, length: 14 }], - wordPattern, -}; -const linkedCursors3 = { - ranges: [{ start: test.markerByName("10").position, length: 4 }, { start: test.markerByName("15").position - 2, length: 4 }], - wordPattern, -}; -const linkedCursors4 = { - ranges: [{ start: test.markerByName("12").position, length: 6 }, { start: test.markerByName("14").position - 3, length: 6 }], - wordPattern, -}; - -verify.linkedEditing( { - "1": linkedCursors1, - "2": linkedCursors1, - "3": linkedCursors1, - "4": linkedCursors1, - "5": linkedCursors1, - "6": linkedCursors2, - "7": linkedCursors2, - "8": linkedCursors2, - "9": linkedCursors2, - "10": linkedCursors3, - "11": linkedCursors3, - "12": linkedCursors4, - "13": linkedCursors4, - "14": linkedCursors4, - "15": linkedCursors3, -}); \ No newline at end of file +verify.baselineLinkedEditing();