From 409d9495301d284b336155312eb9d02921f213f3 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 8 Mar 2023 17:38:17 -0800 Subject: [PATCH 01/32] add tests and fourslash framework --- src/harness/fourslashImpl.ts | 9 ++++ src/harness/fourslashInterfaceImpl.ts | 5 ++ src/services/services.ts | 7 +++ src/services/types.ts | 9 ++++ tests/cases/fourslash/mirrorCursor.ts | 78 +++++++++++++++++++++++++++ 5 files changed, 108 insertions(+) create mode 100644 tests/cases/fourslash/mirrorCursor.ts diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 7c7c43db639e7..e3fef1cec9259 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3444,6 +3444,15 @@ export class TestState { } } + public verifyJsxMirrorCursor(map: {[markerName:string]:ts.JsxMirrorCursorInfo | undefined}):void { + for (const markerName in map) { + this.goToMarker(markerName); + // const actual = {}; + const actual = this.languageService.getJsxMirrorCursorAtPosition(this.activeFile.fileName, this.currentCaretPosition); + assert.deepEqual(actual, map[markerName], markerName); + } + } + 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 4cf5c58d542b7..22541648c28d5 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -189,6 +189,11 @@ export class VerifyNegatable { this.state.verifyJsxClosingTag(map); } + public jsxMirrorCursor(map: { [markerName: string]: ts.JsxMirrorCursorInfo | undefined }): void { + this.state.verifyJsxMirrorCursor(map); + } + + public isInCommentAtPosition(onlyMultiLineDiverges?: boolean) { this.state.verifySpanOfEnclosingComment(this.negative, onlyMultiLineDiverges); } diff --git a/src/services/services.ts b/src/services/services.ts index 4bf03927fb3bf..80358a97a501e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -189,6 +189,7 @@ import { JsxElement, JsxEmit, JsxFragment, + JsxMirrorCursorInfo, LanguageService, LanguageServiceHost, LanguageServiceMode, @@ -2478,6 +2479,11 @@ export function createLanguageService( } } + function getJsxMirrorCursorAtPosition(fileName: string, position: number): JsxMirrorCursorInfo[] | undefined { + return undefined; + // ISABEL unimplemented + } + function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) { return { lineStarts: sourceFile.getLineStarts(), @@ -3009,6 +3015,7 @@ export function createLanguageService( getDocCommentTemplateAtPosition, isValidBraceCompletionAtPosition, getJsxClosingTagAtPosition, + getJsxMirrorCursorAtPosition, getSpanOfEnclosingComment, getCodeFixesAtPosition, getCombinedCodeFix, diff --git a/src/services/types.ts b/src/services/types.ts index de17b02ac2c4d..afbbcfc8f0b50 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -607,6 +607,8 @@ export interface LanguageService { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; + getJsxMirrorCursorAtPosition(fileName: string, currentCaretPosition: number): JsxMirrorCursorInfo | unknown; + //ISABEL incorrect return type getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; @@ -661,6 +663,13 @@ export interface JsxClosingTagInfo { readonly newText: string; } +export interface JsxMirrorCursorInfo { + readonly startLine : number; + readonly startCharacter : number; + readonly endLine : number; + readonly endCharacter : number; +} + export interface CombinedCodeFixScope { type: "file"; fileName: string; } export const enum OrganizeImportsMode { diff --git a/tests/cases/fourslash/mirrorCursor.ts b/tests/cases/fourslash/mirrorCursor.ts new file mode 100644 index 0000000000000..e73164c948652 --- /dev/null +++ b/tests/cases/fourslash/mirrorCursor.ts @@ -0,0 +1,78 @@ +/// + +// @Filename: /basic.tsx +//// const jsx = ( +////
+////
+//// ); + +// @Filename: /attrs.tsx +//// const jsx = ( +////
+////

+//// +////

+////
+//// ); + +// @Filename: /selfClosing.tsx +//// const jsx = ( +////
+////

+//// +////

+////
+//// ); + +// @Filename: /longName.tsx +//// const jsx = ( +//// +//// +//// ); + +// @FileName: /invalid1.tsx +//// const jsx = ( +////
+////
+////
+//// ); + +// @FileName: /invalid2.tsx +//// const jsx = ( +////
+////
+////
+//// ); + +// @FileName: /fragment.tsx +//// const jsx = ( +//// +//// +//// +//// ); + +// @FileName: /mismatchedNames.tsx +//// const A = thing; +//// const B = thing; +//// const jsx = ( +//// +//// +//// ); + + + +verify.jsxMirrorCursor( { + "0": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, + {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 9}], + "1": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, + {startLine: 5, startCharacter: 6, endLine: 5, endCharacter: 9}], + "2": [], + "3": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 25}, + {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 26}], + "4": [], + "5": [{startLine: 2, startCharacter: 9, endLine: 2, endCharacter: 12}, + {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 9}], + "6": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 5}, + {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 6}], + "7": [], +}) \ No newline at end of file From 94c4b6506ab052f6c9397d3aa674e19de1798c8f Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 10 Mar 2023 16:32:05 -0800 Subject: [PATCH 02/32] cd --- lib/tsserverlibrary.d.ts | 16 +++++++++ src/harness/harnessLanguageService.ts | 3 ++ src/server/protocol.ts | 10 ++++++ src/server/session.ts | 9 +++++ src/services/services.ts | 35 +++++++++++++++++-- src/services/types.ts | 5 +++ .../reference/api/tsserverlibrary.d.ts | 4 +++ tests/baselines/reference/api/typescript.d.ts | 8 +++++ tests/cases/fourslash/fourslash.ts | 5 +++ tests/cases/fourslash/mirrorCursor.ts | 24 +++++++------ 10 files changed, 106 insertions(+), 13 deletions(-) diff --git a/lib/tsserverlibrary.d.ts b/lib/tsserverlibrary.d.ts index 0884b10602045..7fab75b0aeb81 100644 --- a/lib/tsserverlibrary.d.ts +++ b/lib/tsserverlibrary.d.ts @@ -92,6 +92,7 @@ declare namespace ts { */ enum CommandTypes { JsxClosingTag = "jsxClosingTag", + JsxMirrorCursor = "jsxMirrorCursor", Brace = "brace", BraceCompletion = "braceCompletion", GetSpanOfEnclosingComment = "getSpanOfEnclosingComment", @@ -882,6 +883,14 @@ declare namespace ts { interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } + // ISABEL correct extends?? + interface JsxMirrorCursorRequest extends FileLocationRequest { + readonly command: CommandTypes.JsxMirrorCursor; + //ISABEL arguments? + } + interface JsxMirrorCursorResponse extends Response { + readonly mirrorCursors: [JsxMirrorCursorInfo]; + } /** * @deprecated * Get occurrences request; value of command field is @@ -3839,6 +3848,7 @@ declare namespace ts { private getSemanticDiagnosticsSync; private getSuggestionDiagnosticsSync; private getJsxClosingTag; + private getJsxMirrorCursor private getDocumentHighlights; private provideInlayHints; private setCompilerOptionsForInferredProjects; @@ -10084,6 +10094,12 @@ declare namespace ts { interface JsxClosingTagInfo { readonly newText: string; } + interface JsxMirrorCursorInfo { + readonly startLine : number; + readonly startCharacter : number; + readonly endLine : number; + readonly endCharacter : number; + } interface CombinedCodeFixScope { type: "file"; fileName: string; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 84eb84e330af6..0f0eea5ea00dc 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -593,6 +593,9 @@ class LanguageServiceShimProxy implements ts.LanguageService { getJsxClosingTagAtPosition(): never { throw new Error("Not supported on the shim."); } + getJsxMirrorCursorAtPosition(): never { + throw new Error("Not supported on the shim."); + } getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): ts.TextSpan { return unwrapJSONCallResult(this.shim.getSpanOfEnclosingComment(fileName, position, onlyMultiLine)); } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 50546bb8dce8f..da01b3354ebac 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -23,6 +23,7 @@ import { export const enum CommandTypes { JsxClosingTag = "jsxClosingTag", + JsxMirrorCursor = "jsxMirrorCursor", Brace = "brace", /** @internal */ BraceFull = "brace-full", @@ -1101,6 +1102,15 @@ export interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } +export interface JsxMirrorCursorRequest extends FileLocationRequest { + readonly command: CommandTypes.JsxMirrorCursor; + // ISABEL? +} + +export interface JsxMirrorCursorResponse extends Response { + // ISABEL? +} + /** * Get document highlights request; value of command field is diff --git a/src/server/session.ts b/src/server/session.ts index f7e37522a8719..74153bb08d741 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1802,6 +1802,11 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } + private getJsxMirrorCursor(args): undefined{ + return undefined; + //ISABEL stub + } + private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): readonly protocol.DocumentHighlightsItem[] | readonly DocumentHighlights[] { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); @@ -3389,6 +3394,10 @@ export class Session implements EventSender { [protocol.CommandTypes.JsxClosingTag]: (request: protocol.JsxClosingTagRequest) => { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); }, + [protocol.CommandTypes.JsxMirrorCursor]: (request: protocol.JsxMirrorCursorRequest) => { + return this.requiredResponse(this.getJsxClosingTag(request.arguments)); + // ISABEL what ; + }, [protocol.CommandTypes.GetCodeFixes]: (request: protocol.CodeFixRequest) => { return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ true)); }, diff --git a/src/services/services.ts b/src/services/services.ts index 80358a97a501e..7aa2f000190dc 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1,3 +1,4 @@ +import { findArgument } from "../jsTyping/shared"; import * as ts from "./_namespaces/ts"; import { __String, @@ -189,6 +190,7 @@ import { JsxElement, JsxEmit, JsxFragment, + JsxLinkedEditInfo, JsxMirrorCursorInfo, LanguageService, LanguageServiceHost, @@ -2479,8 +2481,37 @@ export function createLanguageService( } } - function getJsxMirrorCursorAtPosition(fileName: string, position: number): JsxMirrorCursorInfo[] | undefined { - return undefined; + function getJsxMirrorCursorAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined { + const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); + const token = findPrecedingToken(position, sourceFile); + if (!token) return undefined; + + // if it is not in a jsx element + if (token.parent.parent.kind !== SyntaxKind.JsxElement && token.parent.parent.kind !== SyntaxKind.JsxFragment) return undefined; + + //opening element + if (token.parent.kind === SyntaxKind.JsxOpeningElement){ + const nameSpan = {start : token.pos, length : token.pos - token.end}; + const name = ""; + return {ranges:nameSpan, wordPattern : name}; + } + //losing element + if (token.parent.kind === SyntaxKind.JsxClosingElement){ + return undefined; + } + + //opening fragment + if (token.parent.kind === SyntaxKind.JsxOpeningFragment){ + return undefined; + } + //losing element + if (token.parent.kind === SyntaxKind.JsxClosingFragment){ + return undefined; + } + + + //neither + return undefined // ISABEL unimplemented } diff --git a/src/services/types.ts b/src/services/types.ts index afbbcfc8f0b50..19d17e9970687 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -670,6 +670,11 @@ export interface JsxMirrorCursorInfo { readonly endCharacter : number; } +export interface JsxLinkedEditInfo { + readonly ranges : TextSpan; + wordPattern? : string; +} + export interface CombinedCodeFixScope { type: "file"; fileName: string; } export const enum OrganizeImportsMode { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 6f8b2365c3511..14187beea9385 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -89,6 +89,7 @@ declare namespace ts { namespace protocol { enum CommandTypes { JsxClosingTag = "jsxClosingTag", + JsxMirrorCursor = "jsxMirrorCursor", Brace = "brace", BraceCompletion = "braceCompletion", GetSpanOfEnclosingComment = "getSpanOfEnclosingComment", @@ -877,6 +878,8 @@ declare namespace ts { interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } + interface mirrror {} + // ISABEL /** * Get document highlights request; value of command field is * "documentHighlights". Return response giving spans that are relevant @@ -3847,6 +3850,7 @@ declare namespace ts { private getSemanticDiagnosticsSync; private getSuggestionDiagnosticsSync; private getJsxClosingTag; + private getJsxMirrorCursor; private getDocumentHighlights; private provideInlayHints; private setCompilerOptionsForInferredProjects; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 35f824d416f0b..70518ff7b751e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6111,6 +6111,8 @@ declare namespace ts { toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; + // ISABEL change name? + getMirrorCursorPosition(fileName: string, position: number): JsxMirrorCursorInfo | undefined; applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise; applyCodeActionCommand(action: CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise; @@ -6136,6 +6138,12 @@ declare namespace ts { interface JsxClosingTagInfo { readonly newText: string; } + interface JsxMirrorCursorInfo { + readonly startLine : number; + readonly startCharacter : number; + readonly endLine : number; + readonly endCharacter : number; + } interface CombinedCodeFixScope { type: "file"; fileName: string; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 53352c8573dc1..bffeb276b2a49 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -260,6 +260,11 @@ declare namespace FourSlashInterface { implementationListIsEmpty(): void; isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; + jsxMirrorCursor(map: { [markerName: string]: { + readonly startLine : number; + readonly startCharacter : number; + readonly endLine : number; + readonly endCharacter : number;}[] | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; codeFix(options: { description: string | [string, ...(string | number)[]] | DiagnosticIgnoredInterpolations, diff --git a/tests/cases/fourslash/mirrorCursor.ts b/tests/cases/fourslash/mirrorCursor.ts index e73164c948652..65ad9b54ef6dc 100644 --- a/tests/cases/fourslash/mirrorCursor.ts +++ b/tests/cases/fourslash/mirrorCursor.ts @@ -6,6 +6,7 @@ ////
//// ); + // @Filename: /attrs.tsx //// const jsx = ( ////
@@ -61,18 +62,19 @@ + verify.jsxMirrorCursor( { "0": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 9}], - "1": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, - {startLine: 5, startCharacter: 6, endLine: 5, endCharacter: 9}], - "2": [], - "3": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 25}, - {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 26}], - "4": [], - "5": [{startLine: 2, startCharacter: 9, endLine: 2, endCharacter: 12}, - {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 9}], - "6": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 5}, - {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 6}], - "7": [], + // "1": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, + // {startLine: 5, startCharacter: 6, endLine: 5, endCharacter: 9}], + // "2": [], + // "3": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 25}, + // {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 26}], + // "4": [], + // "5": [{startLine: 2, startCharacter: 9, endLine: 2, endCharacter: 12}, + // {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 9}], + // "6": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 5}, + // {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 6}], + // "7": [], }) \ No newline at end of file From a5192e8823f92a1cac81e3b8f2007dc8277fe389 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 10 Mar 2023 23:45:32 -0800 Subject: [PATCH 03/32] first implementation, all tests except 3 (namespace) passes --- src/harness/fourslashInterfaceImpl.ts | 2 +- src/services/services.ts | 69 +++++++++++----- src/services/types.ts | 2 +- tests/cases/fourslash/mirrorCursor.ts | 115 +++++++++++++------------- 4 files changed, 110 insertions(+), 78 deletions(-) diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 22541648c28d5..89768d69200c9 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -189,7 +189,7 @@ export class VerifyNegatable { this.state.verifyJsxClosingTag(map); } - public jsxMirrorCursor(map: { [markerName: string]: ts.JsxMirrorCursorInfo | undefined }): void { + public jsxMirrorCursor(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined }): void { this.state.verifyJsxMirrorCursor(map); } diff --git a/src/services/services.ts b/src/services/services.ts index 7aa2f000190dc..7c29ebc11b813 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1,4 +1,3 @@ -import { findArgument } from "../jsTyping/shared"; import * as ts from "./_namespaces/ts"; import { __String, @@ -154,6 +153,7 @@ import { isJSDocCommentContainingNode, isJsxAttributes, isJsxClosingElement, + isJsxClosingFragment, isJsxElement, isJsxFragment, isJsxOpeningElement, @@ -2486,33 +2486,62 @@ export function createLanguageService( const token = findPrecedingToken(position, sourceFile); if (!token) return undefined; - // if it is not in a jsx element - if (token.parent.parent.kind !== SyntaxKind.JsxElement && token.parent.parent.kind !== SyntaxKind.JsxFragment) return undefined; + let wordPattern : string | undefined; + // if it is not in a jsx element/fragment, or if the tags do not match + if (isJsxElement(token.parent.parent)){ + if (token.parent.parent.openingElement.tagName.getText() !== token.parent.parent.closingElement.tagName.getText()) { + return undefined; + } + wordPattern = token.parent.parent.openingElement.tagName.getText(); + } + else if (isJsxFragment(token.parent.parent)) { + wordPattern = undefined; + } + else return undefined; + let ranges : {start: number, end: number}; //opening element - if (token.parent.kind === SyntaxKind.JsxOpeningElement){ - const nameSpan = {start : token.pos, length : token.pos - token.end}; - const name = ""; - return {ranges:nameSpan, wordPattern : name}; + if (isJsxOpeningElement(token.parent)){ + ranges = {start : token.parent.parent.closingElement.tagName.pos, end : token.parent.parent.closingElement.tagName.end}; } - //losing element - if (token.parent.kind === SyntaxKind.JsxClosingElement){ - return undefined; + else if (isJsxClosingElement(token.parent)){ + ranges = {start : token.parent.parent.openingElement.tagName.pos, end : token.parent.parent.openingElement.tagName.end}; } - - //opening fragment - if (token.parent.kind === SyntaxKind.JsxOpeningFragment){ - return undefined; + else if (isJsxOpeningFragment(token.parent)){ + const pos = token.parent.parent.closingFragment.pos + 1; + ranges = {start : pos, end : pos}; } - //losing element - if (token.parent.kind === SyntaxKind.JsxClosingFragment){ + else if (isJsxClosingFragment(token.parent)){ + const pos = token.parent.parent.openingFragment.pos + 1; + ranges = {start : pos, end : pos}; + } + else { return undefined; } - - //neither - return undefined - // ISABEL unimplemented + return {ranges, wordPattern}; + + // is this cursed? + // it doesnt even work -> it errors types + // switch (true) { + // case isJsxOpeningElement(token.parent): + // // const nameSpan = {start : token.parent.parent.closingElement.pos, end : token.parent.parent.closingElement.end}; + // case isJsxClosingElement(token.parent): + // case isJsxOpeningFragment(token.parent): + // case isJsxClosingFragment(token.parent): + // return undefined + // } + + // switch (token.parent.kind) { + // case SyntaxKind.JsxOpeningElement: + // // const nameSpan = {start : token.parent.parent.closingElement.pos, end : token.parent.parent.closingElement.end}; + // case SyntaxKind.JsxClosingElement: + // case SyntaxKind.JsxOpeningFragment: + // case SyntaxKind.JsxClosingFragment: + // return undefined + // } + + // ISABEL delete extras } function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) { diff --git a/src/services/types.ts b/src/services/types.ts index 19d17e9970687..13e72c7bf5a6b 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -671,7 +671,7 @@ export interface JsxMirrorCursorInfo { } export interface JsxLinkedEditInfo { - readonly ranges : TextSpan; + readonly ranges : {start: number, end: number}; wordPattern? : string; } diff --git a/tests/cases/fourslash/mirrorCursor.ts b/tests/cases/fourslash/mirrorCursor.ts index 65ad9b54ef6dc..21353dcd949e4 100644 --- a/tests/cases/fourslash/mirrorCursor.ts +++ b/tests/cases/fourslash/mirrorCursor.ts @@ -1,80 +1,83 @@ /// // @Filename: /basic.tsx -//// const jsx = ( -////
-////
-//// ); +////const jsx = ( +////
+////
+////); // @Filename: /attrs.tsx -//// const jsx = ( -////
-////

-//// -////

-////
-//// ); +////const jsx = ( +////
+////

+//// +////

+////
+////); // @Filename: /selfClosing.tsx -//// const jsx = ( -////
-////

-//// -////

-////
-//// ); +////const jsx = ( +////
+////

+//// +////

+////
+////); -// @Filename: /longName.tsx -//// const jsx = ( -//// -//// -//// ); +// @Filename: /namespace.tsx +////const jsx = ( +//// +//// +////); // @FileName: /invalid1.tsx -//// const jsx = ( -////
-////
-////
-//// ); +////const jsx = ( +////
+////
+////
+////); // @FileName: /invalid2.tsx -//// const jsx = ( -////
-////
-////
-//// ); +////const jsx = ( +////
+////
+////
+////); // @FileName: /fragment.tsx -//// const jsx = ( -//// -//// -//// -//// ); +////const jsx = ( +//// +//// +//// +////); // @FileName: /mismatchedNames.tsx -//// const A = thing; -//// const B = thing; -//// const jsx = ( -//// -//// -//// ); - - - +////const A = thing; +////const B = thing; +////const jsx = ( +//// +//// +////); verify.jsxMirrorCursor( { - "0": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, - {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 9}], - // "1": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, + // "0": {ranges: {start: 28, end: 31}, wordPattern : 'div'}, + // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, + // {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 9}], + // "1": {ranges: {start: 91, end: 94}, wordPattern : 'div'}, + // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, // {startLine: 5, startCharacter: 6, endLine: 5, endCharacter: 9}], - // "2": [], - // "3": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 25}, + // "2": undefined, + // "3": {ranges: {start: 0, end: 0}, wordPattern : 'div'}, // FAILS + // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 25}, // {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 26}], - // "4": [], - // "5": [{startLine: 2, startCharacter: 9, endLine: 2, endCharacter: 12}, + // "4": undefined, + // "5": {ranges: {start: 44, end: 47}, wordPattern : 'div'}, + // [{startLine: 2, startCharacter: 9, endLine: 2, endCharacter: 12}, // {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 9}], - // "6": [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 5}, + "6": {ranges: {start: 42, end: 42}, wordPattern : undefined}, // if you dont define wordpattern in 6, it has issues + // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 5}, // {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 6}], - // "7": [], + "7": undefined, + // "8": undefined, }) \ No newline at end of file From 21aa9e76bfca2ad0c3173557b5eb6620b5fd59a4 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 10 Mar 2023 23:47:54 -0800 Subject: [PATCH 04/32] forgot to include into last commit --- tests/cases/fourslash/fourslash.ts | 6 ++---- tests/cases/fourslash/mirrorCursor.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index bffeb276b2a49..ec327371e1be6 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -261,10 +261,8 @@ declare namespace FourSlashInterface { isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; jsxMirrorCursor(map: { [markerName: string]: { - readonly startLine : number; - readonly startCharacter : number; - readonly endLine : number; - readonly endCharacter : number;}[] | undefined }): void; + readonly ranges : TextSpan; + wordPattern? : string ;} | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; codeFix(options: { description: string | [string, ...(string | number)[]] | DiagnosticIgnoredInterpolations, diff --git a/tests/cases/fourslash/mirrorCursor.ts b/tests/cases/fourslash/mirrorCursor.ts index 21353dcd949e4..6220c638f78fc 100644 --- a/tests/cases/fourslash/mirrorCursor.ts +++ b/tests/cases/fourslash/mirrorCursor.ts @@ -61,18 +61,18 @@ ////); verify.jsxMirrorCursor( { - // "0": {ranges: {start: 28, end: 31}, wordPattern : 'div'}, + "0": {ranges: {start: 28, end: 31}, wordPattern : 'div'}, // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, // {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 9}], - // "1": {ranges: {start: 91, end: 94}, wordPattern : 'div'}, + "1": {ranges: {start: 91, end: 94}, wordPattern : 'div'}, // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, // {startLine: 5, startCharacter: 6, endLine: 5, endCharacter: 9}], - // "2": undefined, - // "3": {ranges: {start: 0, end: 0}, wordPattern : 'div'}, // FAILS + "2": undefined, + "3": {ranges: {start: 0, end: 0}, wordPattern : 'div'}, // FAILS // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 25}, // {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 26}], - // "4": undefined, - // "5": {ranges: {start: 44, end: 47}, wordPattern : 'div'}, + "4": undefined, + "5": {ranges: {start: 44, end: 47}, wordPattern : 'div'}, // [{startLine: 2, startCharacter: 9, endLine: 2, endCharacter: 12}, // {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 9}], "6": {ranges: {start: 42, end: 42}, wordPattern : undefined}, // if you dont define wordpattern in 6, it has issues From 398f6739720bd3dea87791033ae95808a89ca867 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 13 Mar 2023 16:24:37 -0700 Subject: [PATCH 05/32] passes tests! --- src/services/services.ts | 81 ++++++++++++++++++--------- tests/cases/fourslash/mirrorCursor.ts | 2 +- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 7c29ebc11b813..7a5f25f8a92bf 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -71,6 +71,7 @@ import { filter, find, FindAllReferences, + findAncestor, findChildOfKind, findPrecedingToken, first, @@ -152,6 +153,7 @@ import { isIntrinsicJsxName, isJSDocCommentContainingNode, isJsxAttributes, + isJsxChild, isJsxClosingElement, isJsxClosingFragment, isJsxElement, @@ -2487,38 +2489,61 @@ export function createLanguageService( if (!token) return undefined; let wordPattern : string | undefined; + let ranges : {start: number, end: number}; + // if it is not in a jsx element/fragment, or if the tags do not match - if (isJsxElement(token.parent.parent)){ - if (token.parent.parent.openingElement.tagName.getText() !== token.parent.parent.closingElement.tagName.getText()) { - return undefined; - } - wordPattern = token.parent.parent.openingElement.tagName.getText(); - } - else if (isJsxFragment(token.parent.parent)) { + if (isJsxFragment(token.parent.parent)) { wordPattern = undefined; - } - else return undefined; - - let ranges : {start: number, end: number}; - //opening element - if (isJsxOpeningElement(token.parent)){ - ranges = {start : token.parent.parent.closingElement.tagName.pos, end : token.parent.parent.closingElement.tagName.end}; - } - else if (isJsxClosingElement(token.parent)){ - ranges = {start : token.parent.parent.openingElement.tagName.pos, end : token.parent.parent.openingElement.tagName.end}; - } - else if (isJsxOpeningFragment(token.parent)){ - const pos = token.parent.parent.closingFragment.pos + 1; - ranges = {start : pos, end : pos}; - } - else if (isJsxClosingFragment(token.parent)){ - const pos = token.parent.parent.openingFragment.pos + 1; - ranges = {start : pos, end : pos}; - } + if (isJsxOpeningFragment(token.parent)){ + const pos = token.parent.parent.closingFragment.pos + 1; + ranges = {start : pos, end : pos}; + } + else if (isJsxClosingFragment(token.parent)){ + const pos = token.parent.parent.openingFragment.pos + 1; + ranges = {start : pos, end : pos}; + } + else return undefined; + } + // else if (isJsxElement(token.parent.parent)){ + // if (token.parent.parent.openingElement.tagName.getText() !== token.parent.parent.closingElement.tagName.getText()) { + // return undefined; + // } + // wordPattern = token.parent.parent.openingElement.tagName.getText(); + // if (isJsxOpeningElement(token.parent)){ + // ranges = {start : token.parent.parent.closingElement.tagName.pos, end : token.parent.parent.closingElement.tagName.end}; + // } + // else if (isJsxClosingElement(token.parent)){ + // ranges = {start : token.parent.parent.openingElement.tagName.pos, end : token.parent.parent.openingElement.tagName.end}; + // } + // else return undefined; + // } else { - return undefined; - } + const tag = findAncestor(token, + (n) => { + // refactor case of? + if (isJsxOpeningElement(n)) return true; + else if (isJsxClosingElement(n)) return true; + else if (isJsxAttributes(n)) return "quit"; + else if (isJsxChild(n)) return "quit"; + return false; + } + ); + if (!tag) return undefined; + if (isJsxOpeningElement(tag)){ + ranges = {start : tag.parent.closingElement.tagName.pos, end : tag.parent.closingElement.tagName.end}; + } + else if (isJsxClosingElement(tag)){ + ranges = {start : tag.parent.openingElement.tagName.pos, end : tag.parent.openingElement.tagName.end}; + } + else return undefined; + + if (tag.parent.openingElement.tagName.getText() === tag.parent.closingElement.tagName.getText()) { + wordPattern = tag.tagName.getText(); + } + else return undefined; + } + return {ranges, wordPattern}; // is this cursed? diff --git a/tests/cases/fourslash/mirrorCursor.ts b/tests/cases/fourslash/mirrorCursor.ts index 6220c638f78fc..29fe466bbe672 100644 --- a/tests/cases/fourslash/mirrorCursor.ts +++ b/tests/cases/fourslash/mirrorCursor.ts @@ -68,7 +68,7 @@ verify.jsxMirrorCursor( { // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, // {startLine: 5, startCharacter: 6, endLine: 5, endCharacter: 9}], "2": undefined, - "3": {ranges: {start: 0, end: 0}, wordPattern : 'div'}, // FAILS + "3": {ranges: {start: 46, end: 65}, wordPattern : 'someNamespace.Thing'}, // FAILS // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 25}, // {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 26}], "4": undefined, From 2bafe8da2de82643ea5f4b8d062588c5b31d26a6 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 15 Mar 2023 12:57:41 -0700 Subject: [PATCH 06/32] change testing structure --- tests/cases/fourslash/jsxTagLinkedEdit1.ts | 7 ++ tests/cases/fourslash/jsxTagLinkedEdit2.ts | 10 +++ tests/cases/fourslash/jsxTagLinkedEdit3.ts | 10 +++ tests/cases/fourslash/jsxTagLinkedEdit4.ts | 7 ++ tests/cases/fourslash/jsxTagLinkedEdit5.ts | 8 +++ tests/cases/fourslash/jsxTagLinkedEdit6.ts | 8 +++ tests/cases/fourslash/jsxTagLinkedEdit7.ts | 8 +++ tests/cases/fourslash/jsxTagLinkedEdit8.ts | 12 ++++ tests/cases/fourslash/mirrorCursor.ts | 83 ---------------------- 9 files changed, 70 insertions(+), 83 deletions(-) create mode 100644 tests/cases/fourslash/jsxTagLinkedEdit1.ts create mode 100644 tests/cases/fourslash/jsxTagLinkedEdit2.ts create mode 100644 tests/cases/fourslash/jsxTagLinkedEdit3.ts create mode 100644 tests/cases/fourslash/jsxTagLinkedEdit4.ts create mode 100644 tests/cases/fourslash/jsxTagLinkedEdit5.ts create mode 100644 tests/cases/fourslash/jsxTagLinkedEdit6.ts create mode 100644 tests/cases/fourslash/jsxTagLinkedEdit7.ts create mode 100644 tests/cases/fourslash/jsxTagLinkedEdit8.ts delete mode 100644 tests/cases/fourslash/mirrorCursor.ts diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts new file mode 100644 index 0000000000000..a113cc757b600 --- /dev/null +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -0,0 +1,7 @@ +/// + +// @Filename: /basic.tsx +////const jsx = ( +//// +//// +////); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/jsxTagLinkedEdit2.ts new file mode 100644 index 0000000000000..335482fa12ac6 --- /dev/null +++ b/tests/cases/fourslash/jsxTagLinkedEdit2.ts @@ -0,0 +1,10 @@ +/// + +// @Filename: /attrs.tsx +////const jsx = ( +////
+////

+//// +////

+////
+////); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/jsxTagLinkedEdit3.ts new file mode 100644 index 0000000000000..306c4584eef6b --- /dev/null +++ b/tests/cases/fourslash/jsxTagLinkedEdit3.ts @@ -0,0 +1,10 @@ +/// + +// @Filename: /selfClosing.tsx +////const jsx = ( +////
+////

+//// +////

+////
+////); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/jsxTagLinkedEdit4.ts new file mode 100644 index 0000000000000..013550daccc71 --- /dev/null +++ b/tests/cases/fourslash/jsxTagLinkedEdit4.ts @@ -0,0 +1,7 @@ +/// + +// @Filename: /namespace.tsx +////const jsx = ( +//// +//// +////); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit5.ts b/tests/cases/fourslash/jsxTagLinkedEdit5.ts new file mode 100644 index 0000000000000..490459440b4ed --- /dev/null +++ b/tests/cases/fourslash/jsxTagLinkedEdit5.ts @@ -0,0 +1,8 @@ +/// + +// @FileName: /invalid1.tsx +////const jsx = ( +////
+////
+////
+////); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit6.ts b/tests/cases/fourslash/jsxTagLinkedEdit6.ts new file mode 100644 index 0000000000000..e242efea77e9d --- /dev/null +++ b/tests/cases/fourslash/jsxTagLinkedEdit6.ts @@ -0,0 +1,8 @@ +/// + +// @FileName: /invalid2.tsx +////const jsx = ( +////
+////
+////
+////); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/jsxTagLinkedEdit7.ts new file mode 100644 index 0000000000000..46df2a4a76055 --- /dev/null +++ b/tests/cases/fourslash/jsxTagLinkedEdit7.ts @@ -0,0 +1,8 @@ +/// + +// @FileName: /fragment.tsx +////const jsx = ( +//// +//// +//// +////); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit8.ts b/tests/cases/fourslash/jsxTagLinkedEdit8.ts new file mode 100644 index 0000000000000..8c6221a424031 --- /dev/null +++ b/tests/cases/fourslash/jsxTagLinkedEdit8.ts @@ -0,0 +1,12 @@ +/// + +// @FileName: /mismatchedNames.tsx +////const A = thing; +////const B = thing; +////const jsx = ( +//// +//// +////); + +// verify.jsxMirrorCursor( { +// "0": {ranges: {start: 28, end: 31}, wordPattern : 'div'}} ); \ No newline at end of file diff --git a/tests/cases/fourslash/mirrorCursor.ts b/tests/cases/fourslash/mirrorCursor.ts deleted file mode 100644 index 29fe466bbe672..0000000000000 --- a/tests/cases/fourslash/mirrorCursor.ts +++ /dev/null @@ -1,83 +0,0 @@ -/// - -// @Filename: /basic.tsx -////const jsx = ( -////
-////
-////); - - -// @Filename: /attrs.tsx -////const jsx = ( -////
-////

-//// -////

-////
-////); - -// @Filename: /selfClosing.tsx -////const jsx = ( -////
-////

-//// -////

-////
-////); - -// @Filename: /namespace.tsx -////const jsx = ( -//// -//// -////); - -// @FileName: /invalid1.tsx -////const jsx = ( -////
-////
-////
-////); - -// @FileName: /invalid2.tsx -////const jsx = ( -////
-////
-////
-////); - -// @FileName: /fragment.tsx -////const jsx = ( -//// -//// -//// -////); - -// @FileName: /mismatchedNames.tsx -////const A = thing; -////const B = thing; -////const jsx = ( -//// -//// -////); - -verify.jsxMirrorCursor( { - "0": {ranges: {start: 28, end: 31}, wordPattern : 'div'}, - // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, - // {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 9}], - "1": {ranges: {start: 91, end: 94}, wordPattern : 'div'}, - // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 8}, - // {startLine: 5, startCharacter: 6, endLine: 5, endCharacter: 9}], - "2": undefined, - "3": {ranges: {start: 46, end: 65}, wordPattern : 'someNamespace.Thing'}, // FAILS - // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 25}, - // {startLine: 2, startCharacter: 6, endLine: 2, endCharacter: 26}], - "4": undefined, - "5": {ranges: {start: 44, end: 47}, wordPattern : 'div'}, - // [{startLine: 2, startCharacter: 9, endLine: 2, endCharacter: 12}, - // {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 9}], - "6": {ranges: {start: 42, end: 42}, wordPattern : undefined}, // if you dont define wordpattern in 6, it has issues - // [{startLine: 1, startCharacter: 5, endLine: 1, endCharacter: 5}, - // {startLine: 3, startCharacter: 6, endLine: 3, endCharacter: 6}], - "7": undefined, - // "8": undefined, -}) \ No newline at end of file From da0ddf1457830994497c34a4d7f178383ea21a92 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 15 Mar 2023 23:29:10 -0700 Subject: [PATCH 07/32] change testing structure --- tests/cases/fourslash/jsxTagLinkedEdit1.ts | 40 ++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts index a113cc757b600..9d295cc57d7c2 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit1.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -1,7 +1,43 @@ /// +// the content of basic.tsx +//const jsx = ( +//
+//
+//); + + // @Filename: /basic.tsx ////const jsx = ( -//// -//// +//// /*7*/ +//// /*9*/ +////); +////const jsx2 = ( +////
+//// < /*8*/ /div> +////); +////const jsx3 = ( +//// +////
////); + + +const linkedCursors = {ranges: [{start: 19, end: 22}, + {start: 30, end: 33}], + wordPattern : 'div'}; + +verify.jsxMirrorCursor( { + "0": linkedCursors, + "1": linkedCursors, + "2": linkedCursors, + "3": linkedCursors, + "4": linkedCursors, + "5": linkedCursors, + "6": undefined, + "7": undefined, + "8": undefined, + "9": undefined, + // "10":undefined, // if still a comment, this case doesnt yet work !!!! + } + ); + From 9d7f32cd2b062271a04dab4d1c56bb4ab462cf83 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 15 Mar 2023 23:37:14 -0700 Subject: [PATCH 08/32] add correct content into tests (and some more test cases) --- tests/cases/fourslash/fourslash.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit1.ts | 23 ++++++++-------- tests/cases/fourslash/jsxTagLinkedEdit2.ts | 18 ++++++++++-- tests/cases/fourslash/jsxTagLinkedEdit3.ts | 10 ++++++- tests/cases/fourslash/jsxTagLinkedEdit4.ts | 32 ++++++++++++++++++++-- tests/cases/fourslash/jsxTagLinkedEdit5.ts | 14 ++++++++-- tests/cases/fourslash/jsxTagLinkedEdit6.ts | 17 +++++++++--- tests/cases/fourslash/jsxTagLinkedEdit7.ts | 29 ++++++++++++++++++-- tests/cases/fourslash/jsxTagLinkedEdit8.ts | 11 ++++++-- tests/cases/fourslash/test.jsx | 2 ++ 10 files changed, 128 insertions(+), 30 deletions(-) create mode 100644 tests/cases/fourslash/test.jsx diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index ec327371e1be6..c0ebbbd49caf4 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -261,7 +261,7 @@ declare namespace FourSlashInterface { isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; jsxMirrorCursor(map: { [markerName: string]: { - readonly ranges : TextSpan; + readonly ranges : {start:number, end:number}[], wordPattern? : string ;} | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; codeFix(options: { diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts index 9d295cc57d7c2..5c622448d91a4 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit1.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -22,22 +22,21 @@ ////); -const linkedCursors = {ranges: [{start: 19, end: 22}, - {start: 30, end: 33}], - wordPattern : 'div'}; +const linkedCursors1 = {ranges: [{start: 19, end: 22}, + {start: 30, end: 33}], + wordPattern : 'div'}; verify.jsxMirrorCursor( { - "0": linkedCursors, - "1": linkedCursors, - "2": linkedCursors, - "3": linkedCursors, - "4": linkedCursors, - "5": linkedCursors, + "0": linkedCursors1, + "1": linkedCursors1, + "2": linkedCursors1, + "3": linkedCursors1, + "4": linkedCursors1, + "5": linkedCursors1, "6": undefined, "7": undefined, "8": undefined, "9": undefined, - // "10":undefined, // if still a comment, this case doesnt yet work !!!! - } - ); + // "10":undefined, // if still a comment, case doesnt yet work !!!! + }); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/jsxTagLinkedEdit2.ts index 335482fa12ac6..7b451b6cfcb54 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit2.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit2.ts @@ -2,9 +2,23 @@ // @Filename: /attrs.tsx ////const jsx = ( -////
+//// /*4*/ ////

//// ////

-////
+//// ////); + +const linkedCursors2 = {ranges: [{start: 18, end: 21}, + {start: 91, end: 94}], + wordPattern : 'div'}; + +verify.jsxMirrorCursor( { + "0": linkedCursors2, + "1": linkedCursors2, + "2": undefined, + "3": linkedCursors2, + "4": undefined, + // "5": undefined, // if still a comment, case doesnt yet work !!!! + "6": undefined, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/jsxTagLinkedEdit3.ts index 306c4584eef6b..2554855e8816a 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit3.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit3.ts @@ -7,4 +7,12 @@ //// ////

////
-////); \ No newline at end of file +////); + +// const linkedCursors3 = {ranges: [{start: 18, end: 21}, +// {start: 91, end: 94}], +// wordPattern : 'div'}; + +verify.jsxMirrorCursor( { + "2": undefined, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/jsxTagLinkedEdit4.ts index 013550daccc71..37d2018a5629a 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit4.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit4.ts @@ -1,7 +1,33 @@ /// -// @Filename: /namespace.tsx +// for readability purposes +//const jsx = ( +// > +//

+// +//

+//
+//); + + +// @Filename: /typeTag.tsx ////const jsx = ( -//// -//// +//// /*4*/> +////

+//// +////

+//// ////); + +const linkedCursors4 = {ranges: [{start: 18, end: 21}, + {start: 69, end: 72}], + wordPattern : 'div'}; + +verify.jsxMirrorCursor( { + "0": linkedCursors4, + "1": linkedCursors4, + // "2": undefined, + "3": undefined, + "4": undefined, + "5": linkedCursors4, // if still a comment, this case doesnt yet work !!!! +}); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit5.ts b/tests/cases/fourslash/jsxTagLinkedEdit5.ts index 490459440b4ed..4ed030a590333 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit5.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit5.ts @@ -3,6 +3,16 @@ // @FileName: /invalid1.tsx ////const jsx = ( ////
-////
-////
+////
+////
////); + +const linkedCursors5 = {ranges: [{start: 33, end: 36}, + {start: 44, end: 47}], + wordPattern : 'div'}; + +verify.jsxMirrorCursor( { + "4": undefined, + "5": linkedCursors5, + "6": linkedCursors5, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit6.ts b/tests/cases/fourslash/jsxTagLinkedEdit6.ts index e242efea77e9d..1f3978a5fb298 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit6.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit6.ts @@ -1,8 +1,17 @@ /// -// @FileName: /invalid2.tsx +// @Filename: /namespace.tsx ////const jsx = ( -////
-////
-////
+//// +//// ////); + +const linkedCursors6 = {ranges: [{start: 19, end: 38}, + {start: 46, end: 65}], + wordPattern : 'someNamespace.Thing'}; + +verify.jsxMirrorCursor( { + "1": linkedCursors6, + "2": linkedCursors6, + "3": linkedCursors6, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/jsxTagLinkedEdit7.ts index 46df2a4a76055..30bb859258df4 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit7.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit7.ts @@ -2,7 +2,32 @@ // @FileName: /fragment.tsx ////const jsx = ( -//// +//// /*7*//*1*/ //// +//// /*8*//*4*/ +////); +////const jsx2 = ( +//// <>/*5*/ +//// +////); +////const jsx3 = ( +//// +////
//// -////); \ No newline at end of file +////); + +const linkedCursors7 = {ranges: [{start: 14, end: 14}, + {start: 43, end: 43}], + wordPattern : undefined}; + +verify.jsxMirrorCursor( { + "0": linkedCursors7, + "1": undefined, + "2": undefined, + "3": linkedCursors7, + "4": undefined, + "5": undefined, + // "6": linkedCursors7, // I don't know what's supposed to happen in this case + "7": undefined, + "8": undefined, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit8.ts b/tests/cases/fourslash/jsxTagLinkedEdit8.ts index 8c6221a424031..97a337540af85 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit8.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit8.ts @@ -4,9 +4,14 @@ ////const A = thing; ////const B = thing; ////const jsx = ( -//// +//// //// ////); -// verify.jsxMirrorCursor( { -// "0": {ranges: {start: 28, end: 31}, wordPattern : 'div'}} ); \ No newline at end of file +// const linkedCursors8 = {ranges: [{start: 14, end: 14}, +// {start: 43, end: 43}], +// wordPattern : }; + +verify.jsxMirrorCursor( { + "8": undefined, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/test.jsx b/tests/cases/fourslash/test.jsx new file mode 100644 index 0000000000000..759c99865a518 --- /dev/null +++ b/tests/cases/fourslash/test.jsx @@ -0,0 +1,2 @@ +const c = (< div> +
) \ No newline at end of file From 1fff99dceeae756853d5efc7a140f686411bb6e6 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 15 Mar 2023 23:41:10 -0700 Subject: [PATCH 09/32] changes to getLinkedEdit.., saving some extra code in comments as well --- src/services/services.ts | 88 ++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 7a5f25f8a92bf..800abfed8c4ef 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -153,13 +153,14 @@ import { isIntrinsicJsxName, isJSDocCommentContainingNode, isJsxAttributes, - isJsxChild, isJsxClosingElement, isJsxClosingFragment, isJsxElement, isJsxFragment, isJsxOpeningElement, isJsxOpeningFragment, + isJsxSelfClosingElement, + isJSXTagName, isJsxText, isLabelName, isLiteralComputedPropertyDeclarationName, @@ -188,12 +189,13 @@ import { JSDocTagInfo, JsonSourceFile, JsxAttributes, + JsxClosingElement, JsxClosingTagInfo, JsxElement, JsxEmit, JsxFragment, JsxLinkedEditInfo, - JsxMirrorCursorInfo, + JsxOpeningElement, LanguageService, LanguageServiceHost, LanguageServiceMode, @@ -2489,55 +2491,53 @@ export function createLanguageService( if (!token) return undefined; let wordPattern : string | undefined; - let ranges : {start: number, end: number}; + let ranges : {start: number, end: number}[] = []; - // if it is not in a jsx element/fragment, or if the tags do not match - if (isJsxFragment(token.parent.parent)) { + // check edge cases <| and >| + if (token.kind===SyntaxKind.GreaterThanToken && isJsxOpeningElement(token.parent)) return undefined; + if (token.kind===SyntaxKind.LessThanToken && isJsxClosingElement(token.parent)) return undefined; + + if ((isJsxOpeningFragment(token.parent) && token.kind===SyntaxKind.LessThanToken) + || (isJsxClosingFragment(token.parent) && token.kind===SyntaxKind.SlashToken)){ + + const openPos = token.parent.parent.openingFragment.pos + 1; + const closePos = token.parent.parent.closingFragment.pos + 2; + + ranges = ranges.concat({start : openPos, end : openPos}); + ranges = ranges.concat({start : closePos, end : closePos}); wordPattern = undefined; - if (isJsxOpeningFragment(token.parent)){ - const pos = token.parent.parent.closingFragment.pos + 1; - ranges = {start : pos, end : pos}; - } - else if (isJsxClosingFragment(token.parent)){ - const pos = token.parent.parent.openingFragment.pos + 1; - ranges = {start : pos, end : pos}; - } - else return undefined; } - // else if (isJsxElement(token.parent.parent)){ - // if (token.parent.parent.openingElement.tagName.getText() !== token.parent.parent.closingElement.tagName.getText()) { - // return undefined; - // } - // wordPattern = token.parent.parent.openingElement.tagName.getText(); - // if (isJsxOpeningElement(token.parent)){ - // ranges = {start : token.parent.parent.closingElement.tagName.pos, end : token.parent.parent.closingElement.tagName.end}; - // } - // else if (isJsxClosingElement(token.parent)){ - // ranges = {start : token.parent.parent.openingElement.tagName.pos, end : token.parent.parent.openingElement.tagName.end}; - // } - // else return undefined; - // } + else if (isJsxFragment(token.parent.parent)) { + return undefined; + } else { - const tag = findAncestor(token, + const tag = + // findAncestor(token, + // (n) => { + // if (isJsxOpeningElement(n) || isJsxClosingElement(n)) return true; + // else if (n.kind===SyntaxKind.LessThanToken || isIdentifier(n) || n.kind === SyntaxKind.ThisKeyword || isPropertyAccessExpression(n)) return false; + //   return "quit"; }); + + findAncestor(token, (n) => { - // refactor case of? - if (isJsxOpeningElement(n)) return true; - else if (isJsxClosingElement(n)) return true; - else if (isJsxAttributes(n)) return "quit"; - else if (isJsxChild(n)) return "quit"; - return false; + if (!n.parent.parent) return "quit"; + else if (isJsxElement(n.parent.parent)) { + if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent) )|| n.kind===SyntaxKind.LessThanToken || n.kind===SyntaxKind.SlashToken) return true; + return "quit"; + } + // return true; + // else if (isJsxClosingElement(n.parent)) return true; + // else if (isJsxAttributes(n)) return "quit"; + // else if (isJsxChild(n)) return "quit"; + else return false; + // else if (n.kind === SyntaxKind.JsxTagNameExpression || n.kind === SyntaxKind.JsxTagNamePropertyAccess) } - ); + )?.parent as JsxOpeningElement | JsxClosingElement | undefined; if (!tag) return undefined; - if (isJsxOpeningElement(tag)){ - ranges = {start : tag.parent.closingElement.tagName.pos, end : tag.parent.closingElement.tagName.end}; - } - else if (isJsxClosingElement(tag)){ - ranges = {start : tag.parent.openingElement.tagName.pos, end : tag.parent.openingElement.tagName.end}; - } - else return undefined; - + ranges = ranges.concat({start : tag.parent.openingElement.tagName.pos, end : tag.parent.openingElement.tagName.end}); + ranges = ranges.concat({start : tag.parent.closingElement.tagName.pos, end : tag.parent.closingElement.tagName.end}); + if (tag.parent.openingElement.tagName.getText() === tag.parent.closingElement.tagName.getText()) { wordPattern = tag.tagName.getText(); } @@ -2545,7 +2545,7 @@ export function createLanguageService( } return {ranges, wordPattern}; - + // return {ranges: }}; // is this cursed? // it doesnt even work -> it errors types // switch (true) { From 0b0541db5e9ede2568f6dfcbb5ebfe0995be0249 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 15 Mar 2023 23:51:39 -0700 Subject: [PATCH 10/32] remove comments, extra code, rename functions --- src/harness/fourslashImpl.ts | 5 +-- src/harness/fourslashInterfaceImpl.ts | 4 +- src/harness/harnessLanguageService.ts | 2 +- src/server/protocol.ts | 8 ++-- src/server/session.ts | 7 +--- src/services/services.ts | 43 +++------------------- src/services/types.ts | 12 +----- tests/cases/fourslash/fourslash.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit1.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit2.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit3.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit4.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit5.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit6.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit7.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit8.ts | 2 +- 16 files changed, 26 insertions(+), 73 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index e3fef1cec9259..6afc33345521e 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3444,11 +3444,10 @@ export class TestState { } } - public verifyJsxMirrorCursor(map: {[markerName:string]:ts.JsxMirrorCursorInfo | undefined}):void { + public verifyJsxLinkedEdit(map: {[markerName:string]:ts.JsxLinkedEditInfo | undefined}):void { for (const markerName in map) { this.goToMarker(markerName); - // const actual = {}; - const actual = this.languageService.getJsxMirrorCursorAtPosition(this.activeFile.fileName, this.currentCaretPosition); + const actual = this.languageService.getJsxLinkedEditAtPosition(this.activeFile.fileName, this.currentCaretPosition); assert.deepEqual(actual, map[markerName], markerName); } } diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 89768d69200c9..d6a56c9280a48 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -189,8 +189,8 @@ export class VerifyNegatable { this.state.verifyJsxClosingTag(map); } - public jsxMirrorCursor(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined }): void { - this.state.verifyJsxMirrorCursor(map); + public jsxLinkedEdit(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined }): void { + this.state.verifyJsxLinkedEdit(map); } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 0f0eea5ea00dc..2ad1a313cb15c 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -593,7 +593,7 @@ class LanguageServiceShimProxy implements ts.LanguageService { getJsxClosingTagAtPosition(): never { throw new Error("Not supported on the shim."); } - getJsxMirrorCursorAtPosition(): never { + getJsxLinkedEditAtPosition(): never { throw new Error("Not supported on the shim."); } getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): ts.TextSpan { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index da01b3354ebac..2f079372761d2 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -23,7 +23,7 @@ import { export const enum CommandTypes { JsxClosingTag = "jsxClosingTag", - JsxMirrorCursor = "jsxMirrorCursor", + JsxLinkedEdit = "jsxLinkedEdit", Brace = "brace", /** @internal */ BraceFull = "brace-full", @@ -1102,12 +1102,12 @@ export interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } -export interface JsxMirrorCursorRequest extends FileLocationRequest { - readonly command: CommandTypes.JsxMirrorCursor; +export interface JsxLinkedEditRequest extends FileLocationRequest { + readonly command: CommandTypes.JsxLinkedEdit; // ISABEL? } -export interface JsxMirrorCursorResponse extends Response { +export interface JsxLinkedEditResponse extends Response { // ISABEL? } diff --git a/src/server/session.ts b/src/server/session.ts index 74153bb08d741..4f4f89d2fb7c1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1802,11 +1802,6 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } - private getJsxMirrorCursor(args): undefined{ - return undefined; - //ISABEL stub - } - private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): readonly protocol.DocumentHighlightsItem[] | readonly DocumentHighlights[] { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); @@ -3394,7 +3389,7 @@ export class Session implements EventSender { [protocol.CommandTypes.JsxClosingTag]: (request: protocol.JsxClosingTagRequest) => { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); }, - [protocol.CommandTypes.JsxMirrorCursor]: (request: protocol.JsxMirrorCursorRequest) => { + [protocol.CommandTypes.JsxLinkedEdit]: (request: protocol.JsxLinkedEditRequest) => { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); // ISABEL what ; }, diff --git a/src/services/services.ts b/src/services/services.ts index 800abfed8c4ef..403a4183dd099 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2485,7 +2485,7 @@ export function createLanguageService( } } - function getJsxMirrorCursorAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined { + function getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); const token = findPrecedingToken(position, sourceFile); if (!token) return undefined; @@ -2511,26 +2511,15 @@ export function createLanguageService( return undefined; } else { - const tag = - // findAncestor(token, - // (n) => { - // if (isJsxOpeningElement(n) || isJsxClosingElement(n)) return true; - // else if (n.kind===SyntaxKind.LessThanToken || isIdentifier(n) || n.kind === SyntaxKind.ThisKeyword || isPropertyAccessExpression(n)) return false; - //   return "quit"; }); - - findAncestor(token, + const tag = findAncestor(token, (n) => { if (!n.parent.parent) return "quit"; else if (isJsxElement(n.parent.parent)) { - if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent) )|| n.kind===SyntaxKind.LessThanToken || n.kind===SyntaxKind.SlashToken) return true; + if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent)) || n.kind===SyntaxKind.LessThanToken || n.kind===SyntaxKind.SlashToken) { + return true }; return "quit"; } - // return true; - // else if (isJsxClosingElement(n.parent)) return true; - // else if (isJsxAttributes(n)) return "quit"; - // else if (isJsxChild(n)) return "quit"; else return false; - // else if (n.kind === SyntaxKind.JsxTagNameExpression || n.kind === SyntaxKind.JsxTagNamePropertyAccess) } )?.parent as JsxOpeningElement | JsxClosingElement | undefined; if (!tag) return undefined; @@ -2545,28 +2534,6 @@ export function createLanguageService( } return {ranges, wordPattern}; - // return {ranges: }}; - // is this cursed? - // it doesnt even work -> it errors types - // switch (true) { - // case isJsxOpeningElement(token.parent): - // // const nameSpan = {start : token.parent.parent.closingElement.pos, end : token.parent.parent.closingElement.end}; - // case isJsxClosingElement(token.parent): - // case isJsxOpeningFragment(token.parent): - // case isJsxClosingFragment(token.parent): - // return undefined - // } - - // switch (token.parent.kind) { - // case SyntaxKind.JsxOpeningElement: - // // const nameSpan = {start : token.parent.parent.closingElement.pos, end : token.parent.parent.closingElement.end}; - // case SyntaxKind.JsxClosingElement: - // case SyntaxKind.JsxOpeningFragment: - // case SyntaxKind.JsxClosingFragment: - // return undefined - // } - - // ISABEL delete extras } function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) { @@ -3100,7 +3067,7 @@ export function createLanguageService( getDocCommentTemplateAtPosition, isValidBraceCompletionAtPosition, getJsxClosingTagAtPosition, - getJsxMirrorCursorAtPosition, + getJsxLinkedEditAtPosition: getJsxLinkedEditAtPosition, getSpanOfEnclosingComment, getCodeFixesAtPosition, getCombinedCodeFix, diff --git a/src/services/types.ts b/src/services/types.ts index 13e72c7bf5a6b..50511ee86619d 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -607,8 +607,7 @@ export interface LanguageService { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxMirrorCursorAtPosition(fileName: string, currentCaretPosition: number): JsxMirrorCursorInfo | unknown; - //ISABEL incorrect return type + getJsxLinkedEditAtPosition(fileName: string, currentCaretPosition: number): JsxLinkedEditInfo | unknown; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; @@ -663,15 +662,8 @@ export interface JsxClosingTagInfo { readonly newText: string; } -export interface JsxMirrorCursorInfo { - readonly startLine : number; - readonly startCharacter : number; - readonly endLine : number; - readonly endCharacter : number; -} - export interface JsxLinkedEditInfo { - readonly ranges : {start: number, end: number}; + readonly ranges : {start: number, end: number}[]; wordPattern? : string; } diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index c0ebbbd49caf4..60de39bd77ffa 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -260,7 +260,7 @@ declare namespace FourSlashInterface { implementationListIsEmpty(): void; isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; - jsxMirrorCursor(map: { [markerName: string]: { + jsxLinkedEdit(map: { [markerName: string]: { readonly ranges : {start:number, end:number}[], wordPattern? : string ;} | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts index 5c622448d91a4..61455ad90ea58 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit1.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -26,7 +26,7 @@ const linkedCursors1 = {ranges: [{start: 19, end: 22}, {start: 30, end: 33}], wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "0": linkedCursors1, "1": linkedCursors1, "2": linkedCursors1, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/jsxTagLinkedEdit2.ts index 7b451b6cfcb54..ab0cbc0e5edde 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit2.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit2.ts @@ -13,7 +13,7 @@ const linkedCursors2 = {ranges: [{start: 18, end: 21}, {start: 91, end: 94}], wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "0": linkedCursors2, "1": linkedCursors2, "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/jsxTagLinkedEdit3.ts index 2554855e8816a..f149f0237e519 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit3.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit3.ts @@ -13,6 +13,6 @@ // {start: 91, end: 94}], // wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "2": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/jsxTagLinkedEdit4.ts index 37d2018a5629a..ae48b86e8db45 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit4.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit4.ts @@ -23,7 +23,7 @@ const linkedCursors4 = {ranges: [{start: 18, end: 21}, {start: 69, end: 72}], wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "0": linkedCursors4, "1": linkedCursors4, // "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit5.ts b/tests/cases/fourslash/jsxTagLinkedEdit5.ts index 4ed030a590333..5d92f9196c615 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit5.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit5.ts @@ -11,7 +11,7 @@ const linkedCursors5 = {ranges: [{start: 33, end: 36}, {start: 44, end: 47}], wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "4": undefined, "5": linkedCursors5, "6": linkedCursors5, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit6.ts b/tests/cases/fourslash/jsxTagLinkedEdit6.ts index 1f3978a5fb298..81138025a9122 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit6.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit6.ts @@ -10,7 +10,7 @@ const linkedCursors6 = {ranges: [{start: 19, end: 38}, {start: 46, end: 65}], wordPattern : 'someNamespace.Thing'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "1": linkedCursors6, "2": linkedCursors6, "3": linkedCursors6, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/jsxTagLinkedEdit7.ts index 30bb859258df4..9d7eea3996857 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit7.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit7.ts @@ -20,7 +20,7 @@ const linkedCursors7 = {ranges: [{start: 14, end: 14}, {start: 43, end: 43}], wordPattern : undefined}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "0": linkedCursors7, "1": undefined, "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit8.ts b/tests/cases/fourslash/jsxTagLinkedEdit8.ts index 97a337540af85..f9617c1fcd4cd 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit8.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit8.ts @@ -12,6 +12,6 @@ // {start: 43, end: 43}], // wordPattern : }; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "8": undefined, }); \ No newline at end of file From b70ee9048c297e3fd9339d9169bdcbd6996ed9f0 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 15 Mar 2023 23:51:39 -0700 Subject: [PATCH 11/32] remove commented code and todos, also renames functions --- src/harness/fourslashImpl.ts | 5 +-- src/harness/fourslashInterfaceImpl.ts | 4 +- src/harness/harnessLanguageService.ts | 2 +- src/server/protocol.ts | 8 ++-- src/server/session.ts | 7 +--- src/services/services.ts | 43 +++------------------- src/services/types.ts | 12 +----- tests/cases/fourslash/fourslash.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit1.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit2.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit3.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit4.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit5.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit6.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit7.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit8.ts | 2 +- 16 files changed, 26 insertions(+), 73 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index e3fef1cec9259..6afc33345521e 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3444,11 +3444,10 @@ export class TestState { } } - public verifyJsxMirrorCursor(map: {[markerName:string]:ts.JsxMirrorCursorInfo | undefined}):void { + public verifyJsxLinkedEdit(map: {[markerName:string]:ts.JsxLinkedEditInfo | undefined}):void { for (const markerName in map) { this.goToMarker(markerName); - // const actual = {}; - const actual = this.languageService.getJsxMirrorCursorAtPosition(this.activeFile.fileName, this.currentCaretPosition); + const actual = this.languageService.getJsxLinkedEditAtPosition(this.activeFile.fileName, this.currentCaretPosition); assert.deepEqual(actual, map[markerName], markerName); } } diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 89768d69200c9..d6a56c9280a48 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -189,8 +189,8 @@ export class VerifyNegatable { this.state.verifyJsxClosingTag(map); } - public jsxMirrorCursor(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined }): void { - this.state.verifyJsxMirrorCursor(map); + public jsxLinkedEdit(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined }): void { + this.state.verifyJsxLinkedEdit(map); } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 0f0eea5ea00dc..2ad1a313cb15c 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -593,7 +593,7 @@ class LanguageServiceShimProxy implements ts.LanguageService { getJsxClosingTagAtPosition(): never { throw new Error("Not supported on the shim."); } - getJsxMirrorCursorAtPosition(): never { + getJsxLinkedEditAtPosition(): never { throw new Error("Not supported on the shim."); } getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): ts.TextSpan { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index da01b3354ebac..2f079372761d2 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -23,7 +23,7 @@ import { export const enum CommandTypes { JsxClosingTag = "jsxClosingTag", - JsxMirrorCursor = "jsxMirrorCursor", + JsxLinkedEdit = "jsxLinkedEdit", Brace = "brace", /** @internal */ BraceFull = "brace-full", @@ -1102,12 +1102,12 @@ export interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } -export interface JsxMirrorCursorRequest extends FileLocationRequest { - readonly command: CommandTypes.JsxMirrorCursor; +export interface JsxLinkedEditRequest extends FileLocationRequest { + readonly command: CommandTypes.JsxLinkedEdit; // ISABEL? } -export interface JsxMirrorCursorResponse extends Response { +export interface JsxLinkedEditResponse extends Response { // ISABEL? } diff --git a/src/server/session.ts b/src/server/session.ts index 74153bb08d741..4f4f89d2fb7c1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1802,11 +1802,6 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } - private getJsxMirrorCursor(args): undefined{ - return undefined; - //ISABEL stub - } - private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): readonly protocol.DocumentHighlightsItem[] | readonly DocumentHighlights[] { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); @@ -3394,7 +3389,7 @@ export class Session implements EventSender { [protocol.CommandTypes.JsxClosingTag]: (request: protocol.JsxClosingTagRequest) => { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); }, - [protocol.CommandTypes.JsxMirrorCursor]: (request: protocol.JsxMirrorCursorRequest) => { + [protocol.CommandTypes.JsxLinkedEdit]: (request: protocol.JsxLinkedEditRequest) => { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); // ISABEL what ; }, diff --git a/src/services/services.ts b/src/services/services.ts index 800abfed8c4ef..403a4183dd099 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2485,7 +2485,7 @@ export function createLanguageService( } } - function getJsxMirrorCursorAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined { + function getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); const token = findPrecedingToken(position, sourceFile); if (!token) return undefined; @@ -2511,26 +2511,15 @@ export function createLanguageService( return undefined; } else { - const tag = - // findAncestor(token, - // (n) => { - // if (isJsxOpeningElement(n) || isJsxClosingElement(n)) return true; - // else if (n.kind===SyntaxKind.LessThanToken || isIdentifier(n) || n.kind === SyntaxKind.ThisKeyword || isPropertyAccessExpression(n)) return false; - //   return "quit"; }); - - findAncestor(token, + const tag = findAncestor(token, (n) => { if (!n.parent.parent) return "quit"; else if (isJsxElement(n.parent.parent)) { - if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent) )|| n.kind===SyntaxKind.LessThanToken || n.kind===SyntaxKind.SlashToken) return true; + if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent)) || n.kind===SyntaxKind.LessThanToken || n.kind===SyntaxKind.SlashToken) { + return true }; return "quit"; } - // return true; - // else if (isJsxClosingElement(n.parent)) return true; - // else if (isJsxAttributes(n)) return "quit"; - // else if (isJsxChild(n)) return "quit"; else return false; - // else if (n.kind === SyntaxKind.JsxTagNameExpression || n.kind === SyntaxKind.JsxTagNamePropertyAccess) } )?.parent as JsxOpeningElement | JsxClosingElement | undefined; if (!tag) return undefined; @@ -2545,28 +2534,6 @@ export function createLanguageService( } return {ranges, wordPattern}; - // return {ranges: }}; - // is this cursed? - // it doesnt even work -> it errors types - // switch (true) { - // case isJsxOpeningElement(token.parent): - // // const nameSpan = {start : token.parent.parent.closingElement.pos, end : token.parent.parent.closingElement.end}; - // case isJsxClosingElement(token.parent): - // case isJsxOpeningFragment(token.parent): - // case isJsxClosingFragment(token.parent): - // return undefined - // } - - // switch (token.parent.kind) { - // case SyntaxKind.JsxOpeningElement: - // // const nameSpan = {start : token.parent.parent.closingElement.pos, end : token.parent.parent.closingElement.end}; - // case SyntaxKind.JsxClosingElement: - // case SyntaxKind.JsxOpeningFragment: - // case SyntaxKind.JsxClosingFragment: - // return undefined - // } - - // ISABEL delete extras } function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) { @@ -3100,7 +3067,7 @@ export function createLanguageService( getDocCommentTemplateAtPosition, isValidBraceCompletionAtPosition, getJsxClosingTagAtPosition, - getJsxMirrorCursorAtPosition, + getJsxLinkedEditAtPosition: getJsxLinkedEditAtPosition, getSpanOfEnclosingComment, getCodeFixesAtPosition, getCombinedCodeFix, diff --git a/src/services/types.ts b/src/services/types.ts index 13e72c7bf5a6b..50511ee86619d 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -607,8 +607,7 @@ export interface LanguageService { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxMirrorCursorAtPosition(fileName: string, currentCaretPosition: number): JsxMirrorCursorInfo | unknown; - //ISABEL incorrect return type + getJsxLinkedEditAtPosition(fileName: string, currentCaretPosition: number): JsxLinkedEditInfo | unknown; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; @@ -663,15 +662,8 @@ export interface JsxClosingTagInfo { readonly newText: string; } -export interface JsxMirrorCursorInfo { - readonly startLine : number; - readonly startCharacter : number; - readonly endLine : number; - readonly endCharacter : number; -} - export interface JsxLinkedEditInfo { - readonly ranges : {start: number, end: number}; + readonly ranges : {start: number, end: number}[]; wordPattern? : string; } diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index c0ebbbd49caf4..60de39bd77ffa 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -260,7 +260,7 @@ declare namespace FourSlashInterface { implementationListIsEmpty(): void; isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; - jsxMirrorCursor(map: { [markerName: string]: { + jsxLinkedEdit(map: { [markerName: string]: { readonly ranges : {start:number, end:number}[], wordPattern? : string ;} | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts index 5c622448d91a4..61455ad90ea58 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit1.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -26,7 +26,7 @@ const linkedCursors1 = {ranges: [{start: 19, end: 22}, {start: 30, end: 33}], wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "0": linkedCursors1, "1": linkedCursors1, "2": linkedCursors1, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/jsxTagLinkedEdit2.ts index 7b451b6cfcb54..ab0cbc0e5edde 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit2.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit2.ts @@ -13,7 +13,7 @@ const linkedCursors2 = {ranges: [{start: 18, end: 21}, {start: 91, end: 94}], wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "0": linkedCursors2, "1": linkedCursors2, "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/jsxTagLinkedEdit3.ts index 2554855e8816a..f149f0237e519 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit3.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit3.ts @@ -13,6 +13,6 @@ // {start: 91, end: 94}], // wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "2": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/jsxTagLinkedEdit4.ts index 37d2018a5629a..ae48b86e8db45 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit4.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit4.ts @@ -23,7 +23,7 @@ const linkedCursors4 = {ranges: [{start: 18, end: 21}, {start: 69, end: 72}], wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "0": linkedCursors4, "1": linkedCursors4, // "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit5.ts b/tests/cases/fourslash/jsxTagLinkedEdit5.ts index 4ed030a590333..5d92f9196c615 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit5.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit5.ts @@ -11,7 +11,7 @@ const linkedCursors5 = {ranges: [{start: 33, end: 36}, {start: 44, end: 47}], wordPattern : 'div'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "4": undefined, "5": linkedCursors5, "6": linkedCursors5, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit6.ts b/tests/cases/fourslash/jsxTagLinkedEdit6.ts index 1f3978a5fb298..81138025a9122 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit6.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit6.ts @@ -10,7 +10,7 @@ const linkedCursors6 = {ranges: [{start: 19, end: 38}, {start: 46, end: 65}], wordPattern : 'someNamespace.Thing'}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "1": linkedCursors6, "2": linkedCursors6, "3": linkedCursors6, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/jsxTagLinkedEdit7.ts index 30bb859258df4..9d7eea3996857 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit7.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit7.ts @@ -20,7 +20,7 @@ const linkedCursors7 = {ranges: [{start: 14, end: 14}, {start: 43, end: 43}], wordPattern : undefined}; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "0": linkedCursors7, "1": undefined, "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit8.ts b/tests/cases/fourslash/jsxTagLinkedEdit8.ts index 97a337540af85..f9617c1fcd4cd 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit8.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit8.ts @@ -12,6 +12,6 @@ // {start: 43, end: 43}], // wordPattern : }; -verify.jsxMirrorCursor( { +verify.jsxLinkedEdit( { "8": undefined, }); \ No newline at end of file From 25393f11eb21227b6de46c0b9e242f6a117465c4 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 16 Mar 2023 00:13:45 -0700 Subject: [PATCH 12/32] revert changes to lib --- lib/tsserverlibrary.d.ts | 16 ---------------- tests/cases/fourslash/test.jsx | 2 -- 2 files changed, 18 deletions(-) delete mode 100644 tests/cases/fourslash/test.jsx diff --git a/lib/tsserverlibrary.d.ts b/lib/tsserverlibrary.d.ts index 7fab75b0aeb81..0884b10602045 100644 --- a/lib/tsserverlibrary.d.ts +++ b/lib/tsserverlibrary.d.ts @@ -92,7 +92,6 @@ declare namespace ts { */ enum CommandTypes { JsxClosingTag = "jsxClosingTag", - JsxMirrorCursor = "jsxMirrorCursor", Brace = "brace", BraceCompletion = "braceCompletion", GetSpanOfEnclosingComment = "getSpanOfEnclosingComment", @@ -883,14 +882,6 @@ declare namespace ts { interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } - // ISABEL correct extends?? - interface JsxMirrorCursorRequest extends FileLocationRequest { - readonly command: CommandTypes.JsxMirrorCursor; - //ISABEL arguments? - } - interface JsxMirrorCursorResponse extends Response { - readonly mirrorCursors: [JsxMirrorCursorInfo]; - } /** * @deprecated * Get occurrences request; value of command field is @@ -3848,7 +3839,6 @@ declare namespace ts { private getSemanticDiagnosticsSync; private getSuggestionDiagnosticsSync; private getJsxClosingTag; - private getJsxMirrorCursor private getDocumentHighlights; private provideInlayHints; private setCompilerOptionsForInferredProjects; @@ -10094,12 +10084,6 @@ declare namespace ts { interface JsxClosingTagInfo { readonly newText: string; } - interface JsxMirrorCursorInfo { - readonly startLine : number; - readonly startCharacter : number; - readonly endLine : number; - readonly endCharacter : number; - } interface CombinedCodeFixScope { type: "file"; fileName: string; diff --git a/tests/cases/fourslash/test.jsx b/tests/cases/fourslash/test.jsx deleted file mode 100644 index 759c99865a518..0000000000000 --- a/tests/cases/fourslash/test.jsx +++ /dev/null @@ -1,2 +0,0 @@ -const c = (< div> -
) \ No newline at end of file From 6ba63211f87629d02bc31cdf4f2a8a1a33415499 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 16 Mar 2023 13:19:17 -0700 Subject: [PATCH 13/32] link everything together (fix session client) --- src/harness/client.ts | 4 ++++ src/server/protocol.ts | 4 ++-- src/server/session.ts | 1 - 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/harness/client.ts b/src/harness/client.ts index 22db85eb4be92..89e6293ce6e7c 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -726,6 +726,10 @@ export class SessionClient implements LanguageService { return notImplemented(); } + getJsxLinkedEditAtPosition(_fileName: string, _currentCaretPosition: number): unknown { + return notImplemented(); + } + getSpanOfEnclosingComment(_fileName: string, _position: number, _onlyMultiLine: boolean): TextSpan { return notImplemented(); } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 2f079372761d2..f02dcf5d14e32 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -4,6 +4,7 @@ import { EndOfLineState, FileExtensionInfo, HighlightSpanKind, + JsxLinkedEditInfo, MapLike, OutliningSpanKind, OutputFile, @@ -1104,11 +1105,10 @@ export interface JsxClosingTagResponse extends Response { export interface JsxLinkedEditRequest extends FileLocationRequest { readonly command: CommandTypes.JsxLinkedEdit; - // ISABEL? } export interface JsxLinkedEditResponse extends Response { - // ISABEL? + readonly info: JsxLinkedEditInfo; } diff --git a/src/server/session.ts b/src/server/session.ts index 4f4f89d2fb7c1..18936cf128d73 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -3391,7 +3391,6 @@ export class Session implements EventSender { }, [protocol.CommandTypes.JsxLinkedEdit]: (request: protocol.JsxLinkedEditRequest) => { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); - // ISABEL what ; }, [protocol.CommandTypes.GetCodeFixes]: (request: protocol.CodeFixRequest) => { return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ true)); From 92875ce464bd8bfb9de5d4945bf2b4b579b61d78 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 16 Mar 2023 14:05:57 -0700 Subject: [PATCH 14/32] missed renames --- tests/baselines/reference/api/tsserverlibrary.d.ts | 4 ++-- tests/baselines/reference/api/typescript.d.ts | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 14187beea9385..c663970c6a7b5 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -89,7 +89,7 @@ declare namespace ts { namespace protocol { enum CommandTypes { JsxClosingTag = "jsxClosingTag", - JsxMirrorCursor = "jsxMirrorCursor", + JsxLinkedEdit = "jsxLinkedEdit", Brace = "brace", BraceCompletion = "braceCompletion", GetSpanOfEnclosingComment = "getSpanOfEnclosingComment", @@ -3850,7 +3850,7 @@ declare namespace ts { private getSemanticDiagnosticsSync; private getSuggestionDiagnosticsSync; private getJsxClosingTag; - private getJsxMirrorCursor; + private getJsxLinkedEdit; private getDocumentHighlights; private provideInlayHints; private setCompilerOptionsForInferredProjects; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 70518ff7b751e..9867c22b5837c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6107,12 +6107,11 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; + getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions; - // ISABEL change name? - getMirrorCursorPosition(fileName: string, position: number): JsxMirrorCursorInfo | undefined; applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise; applyCodeActionCommand(action: CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise; applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise; @@ -6138,11 +6137,9 @@ declare namespace ts { interface JsxClosingTagInfo { readonly newText: string; } - interface JsxMirrorCursorInfo { - readonly startLine : number; - readonly startCharacter : number; - readonly endLine : number; - readonly endCharacter : number; + interface JsxLinkedEditInfo { + ranges : {start: number, end: number}[]; + wordPattern? : string; } interface CombinedCodeFixScope { type: "file"; From 57ef9b8e900205436cf864d0c5a49eba538a2f83 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 16 Mar 2023 14:42:05 -0700 Subject: [PATCH 15/32] linted --- src/harness/fourslashImpl.ts | 2 +- src/services/services.ts | 26 +++++++++++++------------- src/services/types.ts | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 6afc33345521e..fa3e0ec8d0686 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3444,7 +3444,7 @@ export class TestState { } } - public verifyJsxLinkedEdit(map: {[markerName:string]:ts.JsxLinkedEditInfo | undefined}):void { + public verifyJsxLinkedEdit(map: {[markerName: string]: ts.JsxLinkedEditInfo | undefined}): void { for (const markerName in map) { this.goToMarker(markerName); const actual = this.languageService.getJsxLinkedEditAtPosition(this.activeFile.fileName, this.currentCaretPosition); diff --git a/src/services/services.ts b/src/services/services.ts index 403a4183dd099..975b74018188c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2490,8 +2490,8 @@ export function createLanguageService( const token = findPrecedingToken(position, sourceFile); if (!token) return undefined; - let wordPattern : string | undefined; - let ranges : {start: number, end: number}[] = []; + let wordPattern: string | undefined; + let ranges: {start: number, end: number}[] = []; // check edge cases <| and >| if (token.kind===SyntaxKind.GreaterThanToken && isJsxOpeningElement(token.parent)) return undefined; @@ -2503,37 +2503,37 @@ export function createLanguageService( const openPos = token.parent.parent.openingFragment.pos + 1; const closePos = token.parent.parent.closingFragment.pos + 2; - ranges = ranges.concat({start : openPos, end : openPos}); - ranges = ranges.concat({start : closePos, end : closePos}); + ranges = ranges.concat({ start : openPos, end : openPos }); + ranges = ranges.concat({ start : closePos, end : closePos }); wordPattern = undefined; } else if (isJsxFragment(token.parent.parent)) { return undefined; } else { - const tag = findAncestor(token, + const tag = findAncestor(token, (n) => { if (!n.parent.parent) return "quit"; else if (isJsxElement(n.parent.parent)) { if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent)) || n.kind===SyntaxKind.LessThanToken || n.kind===SyntaxKind.SlashToken) { - return true }; + return true; + } return "quit"; } - else return false; + return false; } )?.parent as JsxOpeningElement | JsxClosingElement | undefined; if (!tag) return undefined; - ranges = ranges.concat({start : tag.parent.openingElement.tagName.pos, end : tag.parent.openingElement.tagName.end}); - ranges = ranges.concat({start : tag.parent.closingElement.tagName.pos, end : tag.parent.closingElement.tagName.end}); - + ranges = ranges.concat({ start : tag.parent.openingElement.tagName.pos, end : tag.parent.openingElement.tagName.end }); + ranges = ranges.concat({ start : tag.parent.closingElement.tagName.pos, end : tag.parent.closingElement.tagName.end }); + if (tag.parent.openingElement.tagName.getText() === tag.parent.closingElement.tagName.getText()) { wordPattern = tag.tagName.getText(); } else return undefined; } - - return {ranges, wordPattern}; + return { ranges, wordPattern }; } function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) { @@ -3067,7 +3067,7 @@ export function createLanguageService( getDocCommentTemplateAtPosition, isValidBraceCompletionAtPosition, getJsxClosingTagAtPosition, - getJsxLinkedEditAtPosition: getJsxLinkedEditAtPosition, + getJsxLinkedEditAtPosition, getSpanOfEnclosingComment, getCodeFixesAtPosition, getCombinedCodeFix, diff --git a/src/services/types.ts b/src/services/types.ts index 50511ee86619d..5efc0cd06aee9 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -663,8 +663,8 @@ export interface JsxClosingTagInfo { } export interface JsxLinkedEditInfo { - readonly ranges : {start: number, end: number}[]; - wordPattern? : string; + readonly ranges: {start: number, end: number}[]; + wordPattern?: string; } export interface CombinedCodeFixScope { type: "file"; fileName: string; } From 06afbbd8d4c37e034c9ac8c0d708ca1ca5c7588e Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 16 Mar 2023 16:11:05 -0700 Subject: [PATCH 16/32] update baselines --- .../reference/api/tsserverlibrary.d.ts | 17 ++++++++++++++--- tests/baselines/reference/api/typescript.d.ts | 13 ++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index c663970c6a7b5..a060a5a833a0b 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -878,8 +878,12 @@ declare namespace ts { interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } - interface mirrror {} - // ISABEL + interface JsxLinkedEditRequest extends FileLocationRequest { + readonly command: CommandTypes.JsxLinkedEdit; + } + interface JsxLinkedEditResponse extends Response { + readonly info: JsxLinkedEditInfo; + } /** * Get document highlights request; value of command field is * "documentHighlights". Return response giving spans that are relevant @@ -3850,7 +3854,6 @@ declare namespace ts { private getSemanticDiagnosticsSync; private getSuggestionDiagnosticsSync; private getJsxClosingTag; - private getJsxLinkedEdit; private getDocumentHighlights; private provideInlayHints; private setCompilerOptionsForInferredProjects; @@ -9981,6 +9984,7 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; + getJsxLinkedEditAtPosition(fileName: string, currentCaretPosition: number): JsxLinkedEditInfo | unknown; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; @@ -10010,6 +10014,13 @@ declare namespace ts { interface JsxClosingTagInfo { readonly newText: string; } + interface JsxLinkedEditInfo { + readonly ranges: { + start: number; + end: number; + }[]; + wordPattern?: string; + } interface CombinedCodeFixScope { type: "file"; fileName: string; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 9867c22b5837c..07c32976070a4 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6107,7 +6107,7 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; + getJsxLinkedEditAtPosition(fileName: string, currentCaretPosition: number): JsxLinkedEditInfo | unknown; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; @@ -6137,10 +6137,13 @@ declare namespace ts { interface JsxClosingTagInfo { readonly newText: string; } - interface JsxLinkedEditInfo { - ranges : {start: number, end: number}[]; - wordPattern? : string; - } + interface JsxLinkedEditInfo { + readonly ranges: { + start: number; + end: number; + }[]; + wordPattern?: string; + } interface CombinedCodeFixScope { type: "file"; fileName: string; From f4ec7bd3e0921739b8aac07b4c3835a11b153170 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 17 Mar 2023 01:04:32 -0700 Subject: [PATCH 17/32] debugged whitespace issues --- src/services/services.ts | 16 ++++++------ tests/cases/fourslash/jsxTagLinkedEdit1.ts | 29 ++++++++++++++++------ tests/cases/fourslash/jsxTagLinkedEdit2.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit3.ts | 17 +++++++++---- tests/cases/fourslash/jsxTagLinkedEdit4.ts | 9 ++++--- 5 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 975b74018188c..87dd6fea8580b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -73,6 +73,7 @@ import { FindAllReferences, findAncestor, findChildOfKind, + findNextToken, findPrecedingToken, first, firstDefined, @@ -2493,16 +2494,13 @@ export function createLanguageService( let wordPattern: string | undefined; let ranges: {start: number, end: number}[] = []; - // check edge cases <| and >| - if (token.kind===SyntaxKind.GreaterThanToken && isJsxOpeningElement(token.parent)) return undefined; - if (token.kind===SyntaxKind.LessThanToken && isJsxClosingElement(token.parent)) return undefined; - if ((isJsxOpeningFragment(token.parent) && token.kind===SyntaxKind.LessThanToken) || (isJsxClosingFragment(token.parent) && token.kind===SyntaxKind.SlashToken)){ const openPos = token.parent.parent.openingFragment.pos + 1; const closePos = token.parent.parent.closingFragment.pos + 2; + //TODO: fragments with whitespace? ranges = ranges.concat({ start : openPos, end : openPos }); ranges = ranges.concat({ start : closePos, end : closePos }); wordPattern = undefined; @@ -2515,7 +2513,9 @@ export function createLanguageService( (n) => { if (!n.parent.parent) return "quit"; else if (isJsxElement(n.parent.parent)) { - if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent)) || n.kind===SyntaxKind.LessThanToken || n.kind===SyntaxKind.SlashToken) { + if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent)) + || (n.kind===SyntaxKind.LessThanToken && isJSXTagName(findNextToken(n,n.parent,sourceFile) ?? n)) + || n.kind===SyntaxKind.SlashToken) { return true; } return "quit"; @@ -2525,8 +2525,10 @@ export function createLanguageService( )?.parent as JsxOpeningElement | JsxClosingElement | undefined; if (!tag) return undefined; - ranges = ranges.concat({ start : tag.parent.openingElement.tagName.pos, end : tag.parent.openingElement.tagName.end }); - ranges = ranges.concat({ start : tag.parent.closingElement.tagName.pos, end : tag.parent.closingElement.tagName.end }); + ranges = ranges.concat({ start : tag.parent.openingElement.tagName.getStart(), end : tag.parent.openingElement.tagName.end }); + ranges = ranges.concat({ start : tag.parent.closingElement.tagName.getStart(), end : tag.parent.closingElement.tagName.end }); + + if (!(ranges[0].start <= position && position <= ranges[0].end || ranges[1].start <= position && position <= ranges[1].end)) return undefined; if (tag.parent.openingElement.tagName.getText() === tag.parent.closingElement.tagName.getText()) { wordPattern = tag.tagName.getText(); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts index 61455ad90ea58..4b0b82add7777 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit1.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -6,21 +6,31 @@ //
//); - // @Filename: /basic.tsx ////const jsx = ( //// /*7*/ -//// /*9*/ +//// /*8*/ ////); -////const jsx2 = ( + +// @Filename: /whitespaceInvalidClosing.tsx +////const jsx = ( ////
-//// < /*8*/ /div> +//// < /*9*/ /div> ////); + +// @Filename: /whitespaceOpening.tsx ////const jsx3 = ( -//// -////
+//// /*14*/ +//// ////); +// @Filename: /whitespaceClosing.tsx +////const jsx = ( +//// +//// /*19*/ +////); + + const linkedCursors1 = {ranges: [{start: 19, end: 22}, {start: 30, end: 33}], @@ -36,7 +46,10 @@ verify.jsxLinkedEdit( { "6": undefined, "7": undefined, "8": undefined, - "9": undefined, - // "10":undefined, // if still a comment, case doesnt yet work !!!! + "9": undefined, // I believe this should be an invalid tag + "10": undefined, + "13": undefined, + "15": undefined, + "18": undefined, }); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/jsxTagLinkedEdit2.ts index ab0cbc0e5edde..c7e15e1bbf662 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit2.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit2.ts @@ -19,6 +19,6 @@ verify.jsxLinkedEdit( { "2": undefined, "3": linkedCursors2, "4": undefined, - // "5": undefined, // if still a comment, case doesnt yet work !!!! + "5": undefined, "6": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/jsxTagLinkedEdit3.ts index f149f0237e519..e58fb9a262dc7 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit3.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit3.ts @@ -2,11 +2,11 @@ // @Filename: /selfClosing.tsx ////const jsx = ( -////
-////

-//// -////

-////
+////
/*1*/ +////

/*4*/ +//// /*5*/ +//// /*6*/

/*8*/ +//// /*7*/
////); // const linkedCursors3 = {ranges: [{start: 18, end: 21}, @@ -14,5 +14,12 @@ // wordPattern : 'div'}; verify.jsxLinkedEdit( { + "1": undefined, "2": undefined, + "3": undefined, + "4": undefined, + "5": undefined, + "6": undefined, + "7": undefined, + "8": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/jsxTagLinkedEdit4.ts index ae48b86e8db45..85bcb63bfab9b 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit4.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit4.ts @@ -12,11 +12,11 @@ // @Filename: /typeTag.tsx ////const jsx = ( -//// /*4*/> +//// /*4*/>/*5*/ ////

//// ////

-////
+//// ////); const linkedCursors4 = {ranges: [{start: 18, end: 21}, @@ -26,8 +26,9 @@ const linkedCursors4 = {ranges: [{start: 18, end: 21}, verify.jsxLinkedEdit( { "0": linkedCursors4, "1": linkedCursors4, - // "2": undefined, + "2": undefined, "3": undefined, "4": undefined, - "5": linkedCursors4, // if still a comment, this case doesnt yet work !!!! + "5": undefined, + "6": linkedCursors4, }); \ No newline at end of file From 803b2920eb1da85fdd39f58d25ec69c805e97613 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 20 Mar 2023 14:11:13 -0700 Subject: [PATCH 18/32] fixed some whitespace in code --- src/harness/fourslashImpl.ts | 2 +- src/harness/fourslashInterfaceImpl.ts | 1 - src/server/protocol.ts | 1 - src/services/services.ts | 18 +++++++++--------- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index fa3e0ec8d0686..f50f1b75b0989 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3444,7 +3444,7 @@ export class TestState { } } - public verifyJsxLinkedEdit(map: {[markerName: string]: ts.JsxLinkedEditInfo | undefined}): void { + public verifyJsxLinkedEdit(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined } ): void { for (const markerName in map) { this.goToMarker(markerName); const actual = this.languageService.getJsxLinkedEditAtPosition(this.activeFile.fileName, this.currentCaretPosition); diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index d6a56c9280a48..70b459733d40a 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -193,7 +193,6 @@ export class VerifyNegatable { this.state.verifyJsxLinkedEdit(map); } - public isInCommentAtPosition(onlyMultiLineDiverges?: boolean) { this.state.verifySpanOfEnclosingComment(this.negative, onlyMultiLineDiverges); } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index f02dcf5d14e32..5d250cf8a36b9 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1111,7 +1111,6 @@ export interface JsxLinkedEditResponse extends Response { readonly info: JsxLinkedEditInfo; } - /** * Get document highlights request; value of command field is * "documentHighlights". Return response giving spans that are relevant diff --git a/src/services/services.ts b/src/services/services.ts index 87dd6fea8580b..63e364cb339eb 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2494,15 +2494,15 @@ export function createLanguageService( let wordPattern: string | undefined; let ranges: {start: number, end: number}[] = []; - if ((isJsxOpeningFragment(token.parent) && token.kind===SyntaxKind.LessThanToken) - || (isJsxClosingFragment(token.parent) && token.kind===SyntaxKind.SlashToken)){ + if ((isJsxOpeningFragment(token.parent) && token.kind === SyntaxKind.LessThanToken) + || (isJsxClosingFragment(token.parent) && token.kind === SyntaxKind.SlashToken)){ const openPos = token.parent.parent.openingFragment.pos + 1; const closePos = token.parent.parent.closingFragment.pos + 2; //TODO: fragments with whitespace? - ranges = ranges.concat({ start : openPos, end : openPos }); - ranges = ranges.concat({ start : closePos, end : closePos }); + ranges = ranges.concat({ start: openPos, end: openPos }); + ranges = ranges.concat({ start: closePos, end: closePos }); wordPattern = undefined; } else if (isJsxFragment(token.parent.parent)) { @@ -2510,12 +2510,12 @@ export function createLanguageService( } else { const tag = findAncestor(token, - (n) => { + n => { if (!n.parent.parent) return "quit"; else if (isJsxElement(n.parent.parent)) { if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent)) - || (n.kind===SyntaxKind.LessThanToken && isJSXTagName(findNextToken(n,n.parent,sourceFile) ?? n)) - || n.kind===SyntaxKind.SlashToken) { + || (n.kind === SyntaxKind.LessThanToken && isJSXTagName(findNextToken(n, n.parent, sourceFile) ?? n)) + || n.kind === SyntaxKind.SlashToken) { return true; } return "quit"; @@ -2525,8 +2525,8 @@ export function createLanguageService( )?.parent as JsxOpeningElement | JsxClosingElement | undefined; if (!tag) return undefined; - ranges = ranges.concat({ start : tag.parent.openingElement.tagName.getStart(), end : tag.parent.openingElement.tagName.end }); - ranges = ranges.concat({ start : tag.parent.closingElement.tagName.getStart(), end : tag.parent.closingElement.tagName.end }); + ranges = ranges.concat({ start: tag.parent.openingElement.tagName.getStart(sourceFile), end: tag.parent.openingElement.tagName.end }); + ranges = ranges.concat({ start: tag.parent.closingElement.tagName.getStart(sourceFile), end: tag.parent.closingElement.tagName.end }); if (!(ranges[0].start <= position && position <= ranges[0].end || ranges[1].start <= position && position <= ranges[1].end)) return undefined; From 34f5e40bee0c9fc4fc605dfe15c0f20af5882bb5 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 22 Mar 2023 12:54:38 -0700 Subject: [PATCH 19/32] adds better behavior for whitespace and trivia in fragments --- src/harness/fourslashImpl.ts | 2 +- src/services/services.ts | 12 +++--- tests/cases/fourslash/jsxTagLinkedEdit7.ts | 50 +++++++++++++++++++--- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index f50f1b75b0989..e79354c462d5b 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3444,7 +3444,7 @@ export class TestState { } } - public verifyJsxLinkedEdit(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined } ): void { + public verifyJsxLinkedEdit(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined }): void { for (const markerName in map) { this.goToMarker(markerName); const actual = this.languageService.getJsxLinkedEditAtPosition(this.activeFile.fileName, this.currentCaretPosition); diff --git a/src/services/services.ts b/src/services/services.ts index 63e364cb339eb..dc510250adb4c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2497,13 +2497,15 @@ export function createLanguageService( if ((isJsxOpeningFragment(token.parent) && token.kind === SyntaxKind.LessThanToken) || (isJsxClosingFragment(token.parent) && token.kind === SyntaxKind.SlashToken)){ - const openPos = token.parent.parent.openingFragment.pos + 1; - const closePos = token.parent.parent.closingFragment.pos + 2; + const openPos = token.parent.parent.openingFragment.getStart(sourceFile) + "<".length; + const closePos = token.parent.parent.closingFragment.getStart(sourceFile) + " + if ((position !== openPos) && (position !== closePos)) return undefined; + + ranges = [{ start: openPos, end: openPos }, { start: closePos, end: closePos }]; wordPattern = undefined; + return { ranges, wordPattern }; } else if (isJsxFragment(token.parent.parent)) { return undefined; diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/jsxTagLinkedEdit7.ts index 9d7eea3996857..425178534a70d 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit7.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit7.ts @@ -1,33 +1,69 @@ /// +// plaintext +// const jsx = ( +// <> +// +// +// ); +// const jsx2 = ( +// <> +// +// ); +// const jsx3 = ( +// /* this is comment */Hello +// +// ); + // @FileName: /fragment.tsx ////const jsx = ( -//// /*7*//*1*/ +//// /*5*//*1*/ //// -//// /*8*//*4*/ +//// /*6*//*4*/ ////); ////const jsx2 = ( -//// <>/*5*/ +//// <>/*7*/ //// ////); ////const jsx3 = ( -//// +//// /* this is comment *//*13*//*8*/Hello/*9*/ +//// +////); + +// @FileName: /mismatchedFragment.tsx +////const jsx3 = ( +//// ////
//// ////); -const linkedCursors7 = {ranges: [{start: 14, end: 14}, +const linkedCursors7 = {ranges: [{start: 19, end: 19}, {start: 43, end: 43}], wordPattern : undefined}; -verify.jsxLinkedEdit( { +const linkedCursors7jsx3 = {ranges: [{start: 122, end: 122}, + {start: 153, end: 153}], + wordPattern : undefined}; + +verify.jsxLinkedEdit({ "0": linkedCursors7, "1": undefined, "2": undefined, "3": linkedCursors7, "4": undefined, "5": undefined, - // "6": linkedCursors7, // I don't know what's supposed to happen in this case + "6": undefined, "7": undefined, "8": undefined, + "9": undefined, + "10": linkedCursors7jsx3, + "11": undefined, + "12": undefined, + "13": undefined, + "14": linkedCursors7jsx3, + "15": undefined, + "16": undefined, + "17": undefined, + "18": undefined, + // "A": undefined // I don't know what's supposed to happen in this case }); \ No newline at end of file From 03fa5bb9cbabd5313293c182cbdd9ca3ce598bcd Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 22 Mar 2023 14:28:55 -0700 Subject: [PATCH 20/32] refactor getJsxLinkedEditAtPosition --- src/services/services.ts | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index dc510250adb4c..2ca062ebb5c98 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2491,20 +2491,17 @@ export function createLanguageService( const token = findPrecedingToken(position, sourceFile); if (!token) return undefined; - let wordPattern: string | undefined; - let ranges: {start: number, end: number}[] = []; - if ((isJsxOpeningFragment(token.parent) && token.kind === SyntaxKind.LessThanToken) || (isJsxClosingFragment(token.parent) && token.kind === SyntaxKind.SlashToken)){ - const openPos = token.parent.parent.openingFragment.getStart(sourceFile) + "<".length; - const closePos = token.parent.parent.closingFragment.getStart(sourceFile) + " if ((position !== openPos) && (position !== closePos)) return undefined; - ranges = [{ start: openPos, end: openPos }, { start: closePos, end: closePos }]; - wordPattern = undefined; + const ranges = [{ start: openPos, end: openPos }, { start: closePos, end: closePos }]; + const wordPattern = undefined; return { ranges, wordPattern }; } else if (isJsxFragment(token.parent.parent)) { @@ -2523,21 +2520,20 @@ export function createLanguageService( return "quit"; } return false; - } - )?.parent as JsxOpeningElement | JsxClosingElement | undefined; + }); if (!tag) return undefined; - ranges = ranges.concat({ start: tag.parent.openingElement.tagName.getStart(sourceFile), end: tag.parent.openingElement.tagName.end }); - ranges = ranges.concat({ start: tag.parent.closingElement.tagName.getStart(sourceFile), end: tag.parent.closingElement.tagName.end }); + const element = tag.parent as JsxOpeningElement | JsxClosingElement; + const openTag = { start: element.parent.openingElement.tagName.getStart(sourceFile), end: element.parent.openingElement.tagName.end }; + const endTag = { start: element.parent.closingElement.tagName.getStart(sourceFile), end: element.parent.closingElement.tagName.end }; - if (!(ranges[0].start <= position && position <= ranges[0].end || ranges[1].start <= position && position <= ranges[1].end)) return undefined; + if (!(openTag.start <= position && position <= openTag.end || endTag.start <= position && position <= endTag.end)) return undefined; + if (element.parent.openingElement.tagName.getText(sourceFile) !== element.parent.closingElement.tagName.getText(sourceFile)) return undefined; - if (tag.parent.openingElement.tagName.getText() === tag.parent.closingElement.tagName.getText()) { - wordPattern = tag.tagName.getText(); - } - else return undefined; + const ranges = [openTag, endTag]; + const wordPattern = element.tagName.getText(sourceFile); + return { ranges, wordPattern }; } - return { ranges, wordPattern }; } function getLinesForRange(sourceFile: SourceFile, textRange: TextRange) { From 4faee9927942b6a222a89dc93354f7d8bb46fd13 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Wed, 22 Mar 2023 15:32:47 -0700 Subject: [PATCH 21/32] change type to TextSpan --- src/services/services.ts | 4 ++-- src/services/types.ts | 2 +- tests/baselines/reference/api/tsserverlibrary.d.ts | 5 +---- tests/baselines/reference/api/typescript.d.ts | 5 +---- tests/cases/fourslash/fourslash.ts | 2 +- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 2ca062ebb5c98..73311f85274b3 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2500,7 +2500,7 @@ export function createLanguageService( // only allows mirroring right after opening bracket: <| > if ((position !== openPos) && (position !== closePos)) return undefined; - const ranges = [{ start: openPos, end: openPos }, { start: closePos, end: closePos }]; + const ranges = [{ start: openPos, length: 0 }, { start: closePos, length: 0 }]; const wordPattern = undefined; return { ranges, wordPattern }; } @@ -2530,7 +2530,7 @@ export function createLanguageService( if (!(openTag.start <= position && position <= openTag.end || endTag.start <= position && position <= endTag.end)) return undefined; if (element.parent.openingElement.tagName.getText(sourceFile) !== element.parent.closingElement.tagName.getText(sourceFile)) return undefined; - const ranges = [openTag, endTag]; + const ranges = [{ start: openTag.start, length: openTag.end - openTag.start }, { start: endTag.start, length: endTag.end - endTag.start }]; const wordPattern = element.tagName.getText(sourceFile); return { ranges, wordPattern }; } diff --git a/src/services/types.ts b/src/services/types.ts index 5efc0cd06aee9..dc19e4ad841e2 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -663,7 +663,7 @@ export interface JsxClosingTagInfo { } export interface JsxLinkedEditInfo { - readonly ranges: {start: number, end: number}[]; + readonly ranges: TextSpan[]; wordPattern?: string; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index a060a5a833a0b..5af2b6eb01e2c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -10015,10 +10015,7 @@ declare namespace ts { readonly newText: string; } interface JsxLinkedEditInfo { - readonly ranges: { - start: number; - end: number; - }[]; + readonly ranges: TextSpan[]; wordPattern?: string; } interface CombinedCodeFixScope { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 07c32976070a4..79047d331fd30 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6138,10 +6138,7 @@ declare namespace ts { readonly newText: string; } interface JsxLinkedEditInfo { - readonly ranges: { - start: number; - end: number; - }[]; + readonly ranges: TextSpan[]; wordPattern?: string; } interface CombinedCodeFixScope { diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 60de39bd77ffa..9d218e52ba3c6 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -261,7 +261,7 @@ declare namespace FourSlashInterface { isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; jsxLinkedEdit(map: { [markerName: string]: { - readonly ranges : {start:number, end:number}[], + readonly ranges : TextSpan[], wordPattern? : string ;} | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; codeFix(options: { From 6a1d3dd7af2035f6861d07deaabbbc0a56b2b3f3 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 23 Mar 2023 16:35:25 -0700 Subject: [PATCH 22/32] updated tests to have textspan --- tests/cases/fourslash/fourslash.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit1.ts | 6 ++---- tests/cases/fourslash/jsxTagLinkedEdit2.ts | 4 ++-- tests/cases/fourslash/jsxTagLinkedEdit3.ts | 4 ++-- tests/cases/fourslash/jsxTagLinkedEdit4.ts | 5 ++--- tests/cases/fourslash/jsxTagLinkedEdit5.ts | 4 ++-- tests/cases/fourslash/jsxTagLinkedEdit6.ts | 4 ++-- tests/cases/fourslash/jsxTagLinkedEdit7.ts | 8 ++++---- 8 files changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9d218e52ba3c6..55dc2573701ab 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -261,7 +261,7 @@ declare namespace FourSlashInterface { isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; jsxLinkedEdit(map: { [markerName: string]: { - readonly ranges : TextSpan[], + readonly ranges : { start: number, length: number }[], wordPattern? : string ;} | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; codeFix(options: { diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts index 4b0b82add7777..efa6cf133ded3 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit1.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -30,10 +30,8 @@ //// /*19*/ ////); - - -const linkedCursors1 = {ranges: [{start: 19, end: 22}, - {start: 30, end: 33}], +const linkedCursors1 = {ranges: [{start: 19, length: 3}, + {start: 30, length: 3}], wordPattern : 'div'}; verify.jsxLinkedEdit( { diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/jsxTagLinkedEdit2.ts index c7e15e1bbf662..95f4fcf84977a 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit2.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit2.ts @@ -9,8 +9,8 @@ //// ////); -const linkedCursors2 = {ranges: [{start: 18, end: 21}, - {start: 91, end: 94}], +const linkedCursors2 = {ranges: [{start: 18, length: 3}, + {start: 91, length: 3}], wordPattern : 'div'}; verify.jsxLinkedEdit( { diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/jsxTagLinkedEdit3.ts index e58fb9a262dc7..b056a9534ab00 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit3.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit3.ts @@ -9,8 +9,8 @@ //// /*7*/
////); -// const linkedCursors3 = {ranges: [{start: 18, end: 21}, -// {start: 91, end: 94}], +// const linkedCursors3 = {ranges: [{start: 18, length: 3}, +// {start: 91, length: 3}], // wordPattern : 'div'}; verify.jsxLinkedEdit( { diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/jsxTagLinkedEdit4.ts index 85bcb63bfab9b..4f26b52275dd3 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit4.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit4.ts @@ -9,7 +9,6 @@ //
//); - // @Filename: /typeTag.tsx ////const jsx = ( //// /*4*/>/*5*/ @@ -19,8 +18,8 @@ //// ////); -const linkedCursors4 = {ranges: [{start: 18, end: 21}, - {start: 69, end: 72}], +const linkedCursors4 = {ranges: [{start: 18, length: 3}, + {start: 69, length: 3}], wordPattern : 'div'}; verify.jsxLinkedEdit( { diff --git a/tests/cases/fourslash/jsxTagLinkedEdit5.ts b/tests/cases/fourslash/jsxTagLinkedEdit5.ts index 5d92f9196c615..8860b7b0f0767 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit5.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit5.ts @@ -7,8 +7,8 @@ ////
////); -const linkedCursors5 = {ranges: [{start: 33, end: 36}, - {start: 44, end: 47}], +const linkedCursors5 = {ranges: [{start: 33, length: 3}, + {start: 44, length: 3}], wordPattern : 'div'}; verify.jsxLinkedEdit( { diff --git a/tests/cases/fourslash/jsxTagLinkedEdit6.ts b/tests/cases/fourslash/jsxTagLinkedEdit6.ts index 81138025a9122..169a3cccd27b3 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit6.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit6.ts @@ -6,8 +6,8 @@ //// ////); -const linkedCursors6 = {ranges: [{start: 19, end: 38}, - {start: 46, end: 65}], +const linkedCursors6 = {ranges: [{start: 19, length: 19}, + {start: 46, length: 19}], wordPattern : 'someNamespace.Thing'}; verify.jsxLinkedEdit( { diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/jsxTagLinkedEdit7.ts index 425178534a70d..0f80a753c8995 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit7.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit7.ts @@ -37,12 +37,12 @@ //// ////); -const linkedCursors7 = {ranges: [{start: 19, end: 19}, - {start: 43, end: 43}], +const linkedCursors7 = {ranges: [{start: 19, length: 0}, + {start: 43, length: 0}], wordPattern : undefined}; -const linkedCursors7jsx3 = {ranges: [{start: 122, end: 122}, - {start: 153, end: 153}], +const linkedCursors7jsx3 = {ranges: [{start: 122, length: 0}, + {start: 153, length: 0}], wordPattern : undefined}; verify.jsxLinkedEdit({ From c8ea55908027e5d2d08eb92e7335475e9854438b Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 27 Mar 2023 14:11:07 -0700 Subject: [PATCH 23/32] fix names --- src/harness/client.ts | 2 +- src/server/session.ts | 7 +++++++ src/services/types.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/harness/client.ts b/src/harness/client.ts index 89e6293ce6e7c..07b24a123cdca 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -726,7 +726,7 @@ export class SessionClient implements LanguageService { return notImplemented(); } - getJsxLinkedEditAtPosition(_fileName: string, _currentCaretPosition: number): unknown { + getJsxLinkedEditAtPosition(_fileName: string, _position: number): never { return notImplemented(); } diff --git a/src/server/session.ts b/src/server/session.ts index 18936cf128d73..6429d4c3c9e7d 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -81,6 +81,7 @@ import { isStringLiteralLike, JSDocLinkDisplayPart, JSDocTagInfo, + JsxLinkedEditInfo, LanguageServiceMode, LineAndCharacter, map, @@ -1802,6 +1803,12 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } + private getJsxLinkedEdit(args: protocol.FileLocationRequestArgs): JsxLinkedEditInfo | undefined { + const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); + const position = this.getPositionInFile(args, file); + return languageService.getJsxLinkedEditAtPosition(file, position); + } + private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): readonly protocol.DocumentHighlightsItem[] | readonly DocumentHighlights[] { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); diff --git a/src/services/types.ts b/src/services/types.ts index dc19e4ad841e2..090045b7c141a 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -607,7 +607,7 @@ export interface LanguageService { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxLinkedEditAtPosition(fileName: string, currentCaretPosition: number): JsxLinkedEditInfo | unknown; + getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; From fef7ab6b0dc4a1586dd7e479ddcbef957d1ac7be Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 27 Mar 2023 14:11:07 -0700 Subject: [PATCH 24/32] fix names --- src/harness/client.ts | 2 +- src/server/session.ts | 9 ++++++++- src/services/types.ts | 2 +- tests/baselines/reference/api/tsserverlibrary.d.ts | 3 ++- tests/baselines/reference/api/typescript.d.ts | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/harness/client.ts b/src/harness/client.ts index 89e6293ce6e7c..07b24a123cdca 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -726,7 +726,7 @@ export class SessionClient implements LanguageService { return notImplemented(); } - getJsxLinkedEditAtPosition(_fileName: string, _currentCaretPosition: number): unknown { + getJsxLinkedEditAtPosition(_fileName: string, _position: number): never { return notImplemented(); } diff --git a/src/server/session.ts b/src/server/session.ts index 18936cf128d73..bcfe7e7af2256 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -81,6 +81,7 @@ import { isStringLiteralLike, JSDocLinkDisplayPart, JSDocTagInfo, + JsxLinkedEditInfo, LanguageServiceMode, LineAndCharacter, map, @@ -1802,6 +1803,12 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } + private getJsxLinkedEdit(args: protocol.FileLocationRequestArgs): JsxLinkedEditInfo | undefined { + const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); + const position = this.getPositionInFile(args, file); + return languageService.getJsxLinkedEditAtPosition(file, position); + } + private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): readonly protocol.DocumentHighlightsItem[] | readonly DocumentHighlights[] { const { file, project } = this.getFileAndProject(args); const position = this.getPositionInFile(args, file); @@ -3390,7 +3397,7 @@ export class Session implements EventSender { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); }, [protocol.CommandTypes.JsxLinkedEdit]: (request: protocol.JsxLinkedEditRequest) => { - return this.requiredResponse(this.getJsxClosingTag(request.arguments)); + return this.requiredResponse(this.getJsxLinkedEdit(request.arguments)); }, [protocol.CommandTypes.GetCodeFixes]: (request: protocol.CodeFixRequest) => { return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ true)); diff --git a/src/services/types.ts b/src/services/types.ts index dc19e4ad841e2..090045b7c141a 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -607,7 +607,7 @@ export interface LanguageService { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxLinkedEditAtPosition(fileName: string, currentCaretPosition: number): JsxLinkedEditInfo | unknown; + getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 5af2b6eb01e2c..6c985630f0fad 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3854,6 +3854,7 @@ declare namespace ts { private getSemanticDiagnosticsSync; private getSuggestionDiagnosticsSync; private getJsxClosingTag; + private getJsxLinkedEdit; private getDocumentHighlights; private provideInlayHints; private setCompilerOptionsForInferredProjects; @@ -9984,7 +9985,7 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxLinkedEditAtPosition(fileName: string, currentCaretPosition: number): JsxLinkedEditInfo | unknown; + getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 79047d331fd30..c9b3095a92215 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6107,7 +6107,7 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxLinkedEditAtPosition(fileName: string, currentCaretPosition: number): JsxLinkedEditInfo | unknown; + getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; From be34a1e2185bd81f5b35a7d11fcdfc1cf7106a81 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 30 Mar 2023 23:29:51 -0700 Subject: [PATCH 25/32] update protocol, refactor services --- src/server/protocol.ts | 8 +++++-- src/server/session.ts | 21 +++++++++++++++++-- src/services/services.ts | 17 ++++----------- .../reference/api/tsserverlibrary.d.ts | 6 +++++- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 5d250cf8a36b9..d7dda2d048926 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -4,7 +4,6 @@ import { EndOfLineState, FileExtensionInfo, HighlightSpanKind, - JsxLinkedEditInfo, MapLike, OutliningSpanKind, OutputFile, @@ -1107,8 +1106,13 @@ export interface JsxLinkedEditRequest extends FileLocationRequest { readonly command: CommandTypes.JsxLinkedEdit; } +export interface LinkedEditingRanges { + ranges: TextSpan[]; + wordPattern?: string; +} + export interface JsxLinkedEditResponse extends Response { - readonly info: JsxLinkedEditInfo; + readonly body: LinkedEditingRanges; } /** diff --git a/src/server/session.ts b/src/server/session.ts index bcfe7e7af2256..810b6deeb7c50 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1803,10 +1803,13 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } - private getJsxLinkedEdit(args: protocol.FileLocationRequestArgs): JsxLinkedEditInfo | undefined { + private getJsxLinkedEdit(args: protocol.FileLocationRequestArgs): protocol.LinkedEditingRanges | undefined { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); const position = this.getPositionInFile(args, file); - return languageService.getJsxLinkedEditAtPosition(file, position); + const linkedEditInfo = languageService.getJsxLinkedEditAtPosition(file, position); + const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); + if (scriptInfo === undefined || linkedEditInfo === undefined) return undefined; + return convertLinkedEditInfoToRanges(linkedEditInfo, scriptInfo); } private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): readonly protocol.DocumentHighlightsItem[] | readonly DocumentHighlights[] { @@ -3654,6 +3657,20 @@ function positionToLineOffset(info: ScriptInfoOrConfig, position: number): proto return isConfigFile(info) ? locationFromLineAndCharacter(info.getLineAndCharacterOfPosition(position)) : info.positionToLineOffset(position); } +function convertLinkedEditInfoToRanges(linkedEdit: JsxLinkedEditInfo, scriptInfo: ScriptInfo): protocol.LinkedEditingRanges { + return { + ranges: linkedEdit.ranges.map( + s => { + return { + start: scriptInfo.positionToLineOffset(s.start), + end: scriptInfo.positionToLineOffset(s.start + s.length) + }; + } + ), + wordPattern: linkedEdit.wordPattern, + }; +} + function locationFromLineAndCharacter(lc: LineAndCharacter): protocol.Location { return { line: lc.line + 1, offset: lc.character + 1 }; } diff --git a/src/services/services.ts b/src/services/services.ts index 73311f85274b3..abb443a7ff0f4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -73,7 +73,6 @@ import { FindAllReferences, findAncestor, findChildOfKind, - findNextToken, findPrecedingToken, first, firstDefined, @@ -155,13 +154,10 @@ import { isJSDocCommentContainingNode, isJsxAttributes, isJsxClosingElement, - isJsxClosingFragment, isJsxElement, isJsxFragment, isJsxOpeningElement, isJsxOpeningFragment, - isJsxSelfClosingElement, - isJSXTagName, isJsxText, isLabelName, isLiteralComputedPropertyDeclarationName, @@ -2491,9 +2487,7 @@ export function createLanguageService( const token = findPrecedingToken(position, sourceFile); if (!token) return undefined; - if ((isJsxOpeningFragment(token.parent) && token.kind === SyntaxKind.LessThanToken) - || (isJsxClosingFragment(token.parent) && token.kind === SyntaxKind.SlashToken)){ - + if (isJsxFragment(token.parent.parent)) { const openPos = token.parent.parent.openingFragment.getStart(sourceFile) + 1; // "<".length const closePos = token.parent.parent.closingFragment.getStart(sourceFile) + 2; // " { if (!n.parent.parent) return "quit"; else if (isJsxElement(n.parent.parent)) { - if ((isJSXTagName(n) && !isJsxSelfClosingElement(n.parent)) - || (n.kind === SyntaxKind.LessThanToken && isJSXTagName(findNextToken(n, n.parent, sourceFile) ?? n)) - || n.kind === SyntaxKind.SlashToken) { + if (isJsxOpeningElement(n.parent) || isJsxClosingElement(n.parent)) { return true; } return "quit"; @@ -2527,6 +2517,7 @@ export function createLanguageService( const openTag = { start: element.parent.openingElement.tagName.getStart(sourceFile), end: element.parent.openingElement.tagName.end }; const endTag = { start: element.parent.closingElement.tagName.getStart(sourceFile), end: element.parent.closingElement.tagName.end }; + // only return a mirror if the cursor is within a tag name if (!(openTag.start <= position && position <= openTag.end || endTag.start <= position && position <= endTag.end)) return undefined; if (element.parent.openingElement.tagName.getText(sourceFile) !== element.parent.closingElement.tagName.getText(sourceFile)) return undefined; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 6c985630f0fad..3439978eceb58 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -881,8 +881,12 @@ declare namespace ts { interface JsxLinkedEditRequest extends FileLocationRequest { readonly command: CommandTypes.JsxLinkedEdit; } + interface LinkedEditingRanges { + ranges: TextSpan[]; + wordPattern?: string; + } interface JsxLinkedEditResponse extends Response { - readonly info: JsxLinkedEditInfo; + readonly body: LinkedEditingRanges; } /** * Get document highlights request; value of command field is From 7f2f885abfa48cba84b690ac63788f7a36b68c97 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 31 Mar 2023 14:08:21 -0700 Subject: [PATCH 26/32] cleaned up tests --- src/services/services.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit1.ts | 56 ++++++++++++++-------- tests/cases/fourslash/jsxTagLinkedEdit2.ts | 30 ++++++++---- tests/cases/fourslash/jsxTagLinkedEdit3.ts | 11 ++--- tests/cases/fourslash/jsxTagLinkedEdit4.ts | 17 ++++--- tests/cases/fourslash/jsxTagLinkedEdit5.ts | 21 ++++---- tests/cases/fourslash/jsxTagLinkedEdit6.ts | 13 +++-- tests/cases/fourslash/jsxTagLinkedEdit7.ts | 44 ++++++++--------- tests/cases/fourslash/jsxTagLinkedEdit8.ts | 4 -- 9 files changed, 115 insertions(+), 83 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index abb443a7ff0f4..ca18f43389739 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2499,7 +2499,7 @@ export function createLanguageService( return { ranges, wordPattern }; } else { - // looks mirroring in element tag names + // looks for mirroring in element tag names const tag = findAncestor(token, n => { if (!n.parent.parent) return "quit"; diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts index efa6cf133ded3..f9ff4c0b2805f 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit1.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -8,8 +8,8 @@ // @Filename: /basic.tsx ////const jsx = ( -//// /*7*/ -//// /*8*/ +//// /*3*/ +//// /*8*/ ////); // @Filename: /whitespaceInvalidClosing.tsx @@ -18,36 +18,54 @@ //// < /*9*/ /div> ////); -// @Filename: /whitespaceOpening.tsx -////const jsx3 = ( +// @Filename: /whitespace.tsx +////const whitespaceOpening = ( //// /*14*/ -//// +//// ////); - -// @Filename: /whitespaceClosing.tsx -////const jsx = ( -//// -//// /*19*/ +////const whitespaceClosing = ( +//// +//// /*23*/ ////); -const linkedCursors1 = {ranges: [{start: 19, length: 3}, - {start: 30, length: 3}], - wordPattern : 'div'}; +const markers = test.markers(); +const linkedCursors1 = { + ranges: [{ start: markers[0].position, length: 3 }, { start: markers[5].position, length: 3 }], + wordPattern: 'div' +}; +const linkedCursors2 = { + ranges: [{ start: markers[11].position, length: 3 }, { start: markers[15].position, length: 3 }], + wordPattern: 'div' +}; +const linkedCursors3 = { + ranges: [{ start: markers[17].position, length: 3 }, { start: markers[20].position, length: 3 }], + wordPattern: 'div' +}; verify.jsxLinkedEdit( { "0": linkedCursors1, "1": linkedCursors1, "2": linkedCursors1, - "3": linkedCursors1, - "4": linkedCursors1, + "3": undefined, + "4": undefined, "5": linkedCursors1, - "6": undefined, - "7": undefined, + "6": linkedCursors1, + "7": linkedCursors1, "8": undefined, "9": undefined, // I believe this should be an invalid tag "10": undefined, + "11": linkedCursors2, + "12": linkedCursors2, "13": undefined, - "15": undefined, - "18": undefined, + "14": undefined, + "15": linkedCursors2, + "16": linkedCursors2, + "17": linkedCursors3, + "18": linkedCursors3, + "19": undefined, + "20": linkedCursors3, + "21": linkedCursors3, + "22": undefined, + "23": undefined, }); diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/jsxTagLinkedEdit2.ts index 95f4fcf84977a..4a951630c19e7 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit2.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit2.ts @@ -1,24 +1,36 @@ /// +// for readability +////const jsx = ( +////
+////

+//// +////

+////
+////); + // @Filename: /attrs.tsx ////const jsx = ( -//// /*4*/ +//// /*5*/ ////

//// ////

-////
+//// ////); -const linkedCursors2 = {ranges: [{start: 18, length: 3}, - {start: 91, length: 3}], - wordPattern : 'div'}; +const startPos = test.markerByName("0").position; +const endPos = test.markerByName("6").position - 2; +const linkedCursors = { + ranges: [{ start: startPos, length: 3 }, { start: endPos, length: 3 }], + wordPattern : 'div' +}; verify.jsxLinkedEdit( { - "0": linkedCursors2, - "1": linkedCursors2, + "0": linkedCursors, + "1": linkedCursors, "2": undefined, - "3": linkedCursors2, + "3": undefined, "4": undefined, "5": undefined, - "6": undefined, + "6": linkedCursors, }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/jsxTagLinkedEdit3.ts index b056a9534ab00..c732f45624861 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit3.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit3.ts @@ -2,18 +2,16 @@ // @Filename: /selfClosing.tsx ////const jsx = ( -////
/*1*/ +////
/*0*/ ////

/*4*/ -//// /*5*/ +//// No lin/*9*/ked cursors here! +//// /*5*/ //// /*6*/

/*8*/ //// /*7*/
////); -// const linkedCursors3 = {ranges: [{start: 18, length: 3}, -// {start: 91, length: 3}], -// wordPattern : 'div'}; - verify.jsxLinkedEdit( { + "0": undefined, "1": undefined, "2": undefined, "3": undefined, @@ -22,4 +20,5 @@ verify.jsxLinkedEdit( { "6": undefined, "7": undefined, "8": undefined, + "9": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/jsxTagLinkedEdit4.ts index 4f26b52275dd3..d71e0cec1165a 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit4.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit4.ts @@ -15,19 +15,22 @@ ////

//// ////

-//// +//// ////); -const linkedCursors4 = {ranges: [{start: 18, length: 3}, - {start: 69, length: 3}], - wordPattern : 'div'}; +const startPos = test.markerByName("0").position; +const endPos = test.markerByName("6").position; +const linkedCursors = { + ranges: [{ start: startPos, length: 3 }, { start: endPos, length: 3 }], + wordPattern : 'div' +}; verify.jsxLinkedEdit( { - "0": linkedCursors4, - "1": linkedCursors4, + "0": linkedCursors, + "1": linkedCursors, "2": undefined, "3": undefined, "4": undefined, "5": undefined, - "6": linkedCursors4, + "6": linkedCursors, }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit5.ts b/tests/cases/fourslash/jsxTagLinkedEdit5.ts index 8860b7b0f0767..e6925ff4377f7 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit5.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit5.ts @@ -2,17 +2,20 @@ // @FileName: /invalid1.tsx ////const jsx = ( -////
-////
-////
+////
+////
+////
////); -const linkedCursors5 = {ranges: [{start: 33, length: 3}, - {start: 44, length: 3}], - wordPattern : 'div'}; +const startPos = test.markerByName("1").position - 3; +const endPos = test.markerByName("2").position - 3; +const linkedCursors = { + ranges: [{ start: startPos, length: 3 }, { start: endPos, length: 3 }], + wordPattern : 'div' +}; verify.jsxLinkedEdit( { - "4": undefined, - "5": linkedCursors5, - "6": linkedCursors5, + "0": undefined, + "1": linkedCursors, + "2": linkedCursors, }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit6.ts b/tests/cases/fourslash/jsxTagLinkedEdit6.ts index 169a3cccd27b3..e6231a7dbc9ce 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit6.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit6.ts @@ -2,13 +2,16 @@ // @Filename: /namespace.tsx ////const jsx = ( -//// -//// +//// +//// ////); -const linkedCursors6 = {ranges: [{start: 19, length: 19}, - {start: 46, length: 19}], - wordPattern : 'someNamespace.Thing'}; +const startPos = test.markerByName("start").position; +const endPos = test.markerByName("end").position; +const linkedCursors6 = { + ranges: [{ start: startPos, length: 19 }, { start: endPos, length: 19 }], + wordPattern : 'someNamespace.Thing' +}; verify.jsxLinkedEdit( { "1": linkedCursors6, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/jsxTagLinkedEdit7.ts index 0f80a753c8995..059ec9e0e0135 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit7.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit7.ts @@ -7,13 +7,13 @@ // // ); // const jsx2 = ( -// <> -// -// ); -// const jsx3 = ( // /* this is comment */Hello // // ); +// const jsx3 = ( +// <> +// +// ); // @FileName: /fragment.tsx ////const jsx = ( @@ -22,48 +22,46 @@ //// /*6*//*4*/ ////); ////const jsx2 = ( -//// <>/*7*/ -//// -////); -////const jsx3 = ( //// /* this is comment *//*13*//*8*/Hello/*9*/ //// ////); - -// @FileName: /mismatchedFragment.tsx ////const jsx3 = ( -//// -////
+//// <>/*7*/ //// ////); -const linkedCursors7 = {ranges: [{start: 19, length: 0}, - {start: 43, length: 0}], - wordPattern : undefined}; +const startPos1 = test.markerByName("0").position; +const endPos1 = test.markerByName("3").position; +const linkedCursors1 = { + ranges: [{ start: startPos1, length: 0 }, { start: endPos1, length: 0 }], + wordPattern : undefined +}; -const linkedCursors7jsx3 = {ranges: [{start: 122, length: 0}, - {start: 153, length: 0}], - wordPattern : undefined}; +const startPos2 = test.markerByName("10").position; +const endPos2 = test.markerByName("14").position; +const linkedCursors2 = { + ranges: [{ start: startPos2, length: 0 }, { start: endPos2, length: 0 }], + wordPattern : undefined +}; verify.jsxLinkedEdit({ - "0": linkedCursors7, + "0": linkedCursors1, "1": undefined, "2": undefined, - "3": linkedCursors7, + "3": linkedCursors1, "4": undefined, "5": undefined, "6": undefined, "7": undefined, "8": undefined, "9": undefined, - "10": linkedCursors7jsx3, + "10": linkedCursors2, "11": undefined, "12": undefined, "13": undefined, - "14": linkedCursors7jsx3, + "14": linkedCursors2, "15": undefined, "16": undefined, "17": undefined, "18": undefined, - // "A": undefined // I don't know what's supposed to happen in this case }); \ No newline at end of file diff --git a/tests/cases/fourslash/jsxTagLinkedEdit8.ts b/tests/cases/fourslash/jsxTagLinkedEdit8.ts index f9617c1fcd4cd..4d5584a5a9933 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit8.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit8.ts @@ -8,10 +8,6 @@ //// ////); -// const linkedCursors8 = {ranges: [{start: 14, end: 14}, -// {start: 43, end: 43}], -// wordPattern : }; - verify.jsxLinkedEdit( { "8": undefined, }); \ No newline at end of file From 516449577ef5d853dbcfe55e6ef2c3423149fda9 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 31 Mar 2023 14:54:12 -0700 Subject: [PATCH 27/32] rename to match lsp --- src/harness/client.ts | 2 +- src/harness/fourslashImpl.ts | 4 ++-- src/harness/fourslashInterfaceImpl.ts | 4 ++-- src/harness/harnessLanguageService.ts | 2 +- src/server/protocol.ts | 8 ++++---- src/server/session.ts | 12 ++++++------ src/services/services.ts | 6 +++--- src/services/types.ts | 4 ++-- tests/baselines/reference/api/tsserverlibrary.d.ts | 14 +++++++------- tests/baselines/reference/api/typescript.d.ts | 4 ++-- tests/cases/fourslash/fourslash.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit1.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit2.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit3.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit4.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit5.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit6.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit7.ts | 2 +- tests/cases/fourslash/jsxTagLinkedEdit8.ts | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/harness/client.ts b/src/harness/client.ts index 07b24a123cdca..7c7bdfcb36e54 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -726,7 +726,7 @@ export class SessionClient implements LanguageService { return notImplemented(); } - getJsxLinkedEditAtPosition(_fileName: string, _position: number): never { + getLinkedEditingAtPosition(_fileName: string, _position: number): never { return notImplemented(); } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index e79354c462d5b..59437024a0c05 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3444,10 +3444,10 @@ export class TestState { } } - public verifyJsxLinkedEdit(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined }): void { + public verifyLinkedEditing(map: { [markerName: string]: ts.LinkedEditingInfo | undefined }): void { for (const markerName in map) { this.goToMarker(markerName); - const actual = this.languageService.getJsxLinkedEditAtPosition(this.activeFile.fileName, this.currentCaretPosition); + const actual = this.languageService.getLinkedEditingAtPosition(this.activeFile.fileName, this.currentCaretPosition); assert.deepEqual(actual, map[markerName], markerName); } } diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 70b459733d40a..d3306b8b57aa1 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -189,8 +189,8 @@ export class VerifyNegatable { this.state.verifyJsxClosingTag(map); } - public jsxLinkedEdit(map: { [markerName: string]: ts.JsxLinkedEditInfo | undefined }): void { - this.state.verifyJsxLinkedEdit(map); + public linkedEditing(map: { [markerName: string]: ts.LinkedEditingInfo | undefined }): void { + this.state.verifyLinkedEditing(map); } public isInCommentAtPosition(onlyMultiLineDiverges?: boolean) { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 2ad1a313cb15c..c6f95d66ad4d6 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -593,7 +593,7 @@ class LanguageServiceShimProxy implements ts.LanguageService { getJsxClosingTagAtPosition(): never { throw new Error("Not supported on the shim."); } - getJsxLinkedEditAtPosition(): never { + getLinkedEditingAtPosition(): never { throw new Error("Not supported on the shim."); } getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): ts.TextSpan { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index d7dda2d048926..3dd2790198078 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -23,7 +23,7 @@ import { export const enum CommandTypes { JsxClosingTag = "jsxClosingTag", - JsxLinkedEdit = "jsxLinkedEdit", + LinkedEditing = "LinkedEditing", Brace = "brace", /** @internal */ BraceFull = "brace-full", @@ -1102,8 +1102,8 @@ export interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } -export interface JsxLinkedEditRequest extends FileLocationRequest { - readonly command: CommandTypes.JsxLinkedEdit; +export interface LinkedEditingRequest extends FileLocationRequest { + readonly command: CommandTypes.LinkedEditing; } export interface LinkedEditingRanges { @@ -1111,7 +1111,7 @@ export interface LinkedEditingRanges { wordPattern?: string; } -export interface JsxLinkedEditResponse extends Response { +export interface LinkedEditingResponse extends Response { readonly body: LinkedEditingRanges; } diff --git a/src/server/session.ts b/src/server/session.ts index 810b6deeb7c50..a8269fcee6717 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -81,9 +81,9 @@ import { isStringLiteralLike, JSDocLinkDisplayPart, JSDocTagInfo, - JsxLinkedEditInfo, LanguageServiceMode, LineAndCharacter, + LinkedEditingInfo, map, mapDefined, mapDefinedIterator, @@ -1803,10 +1803,10 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } - private getJsxLinkedEdit(args: protocol.FileLocationRequestArgs): protocol.LinkedEditingRanges | undefined { + private getLinkedEditing(args: protocol.FileLocationRequestArgs): protocol.LinkedEditingRanges | undefined { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); const position = this.getPositionInFile(args, file); - const linkedEditInfo = languageService.getJsxLinkedEditAtPosition(file, position); + const linkedEditInfo = languageService.getLinkedEditingAtPosition(file, position); const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); if (scriptInfo === undefined || linkedEditInfo === undefined) return undefined; return convertLinkedEditInfoToRanges(linkedEditInfo, scriptInfo); @@ -3399,8 +3399,8 @@ export class Session implements EventSender { [protocol.CommandTypes.JsxClosingTag]: (request: protocol.JsxClosingTagRequest) => { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); }, - [protocol.CommandTypes.JsxLinkedEdit]: (request: protocol.JsxLinkedEditRequest) => { - return this.requiredResponse(this.getJsxLinkedEdit(request.arguments)); + [protocol.CommandTypes.LinkedEditing]: (request: protocol.LinkedEditingRequest) => { + return this.requiredResponse(this.getLinkedEditing(request.arguments)); }, [protocol.CommandTypes.GetCodeFixes]: (request: protocol.CodeFixRequest) => { return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ true)); @@ -3657,7 +3657,7 @@ function positionToLineOffset(info: ScriptInfoOrConfig, position: number): proto return isConfigFile(info) ? locationFromLineAndCharacter(info.getLineAndCharacterOfPosition(position)) : info.positionToLineOffset(position); } -function convertLinkedEditInfoToRanges(linkedEdit: JsxLinkedEditInfo, scriptInfo: ScriptInfo): protocol.LinkedEditingRanges { +function convertLinkedEditInfoToRanges(linkedEdit: LinkedEditingInfo, scriptInfo: ScriptInfo): protocol.LinkedEditingRanges { return { ranges: linkedEdit.ranges.map( s => { diff --git a/src/services/services.ts b/src/services/services.ts index ca18f43389739..d3ef99f9bc1cf 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -191,7 +191,6 @@ import { JsxElement, JsxEmit, JsxFragment, - JsxLinkedEditInfo, JsxOpeningElement, LanguageService, LanguageServiceHost, @@ -201,6 +200,7 @@ import { length, LineAndCharacter, lineBreakPart, + LinkedEditingInfo, LiteralType, map, mapDefined, @@ -2482,7 +2482,7 @@ export function createLanguageService( } } - function getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined { + function getLinkedEditingAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); const token = findPrecedingToken(position, sourceFile); if (!token) return undefined; @@ -3058,7 +3058,7 @@ export function createLanguageService( getDocCommentTemplateAtPosition, isValidBraceCompletionAtPosition, getJsxClosingTagAtPosition, - getJsxLinkedEditAtPosition, + getLinkedEditingAtPosition, getSpanOfEnclosingComment, getCodeFixesAtPosition, getCombinedCodeFix, diff --git a/src/services/types.ts b/src/services/types.ts index 090045b7c141a..222e2d69e848a 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -607,7 +607,7 @@ export interface LanguageService { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; + getLinkedEditingAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; @@ -662,7 +662,7 @@ export interface JsxClosingTagInfo { readonly newText: string; } -export interface JsxLinkedEditInfo { +export interface LinkedEditingInfo { readonly ranges: TextSpan[]; wordPattern?: string; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 3439978eceb58..28eb0c067aa12 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -89,7 +89,7 @@ declare namespace ts { namespace protocol { enum CommandTypes { JsxClosingTag = "jsxClosingTag", - JsxLinkedEdit = "jsxLinkedEdit", + LinkedEditing = "LinkedEditing", Brace = "brace", BraceCompletion = "braceCompletion", GetSpanOfEnclosingComment = "getSpanOfEnclosingComment", @@ -878,14 +878,14 @@ declare namespace ts { interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } - interface JsxLinkedEditRequest extends FileLocationRequest { - readonly command: CommandTypes.JsxLinkedEdit; + interface LinkedEditingRequest extends FileLocationRequest { + readonly command: CommandTypes.LinkedEditing; } interface LinkedEditingRanges { ranges: TextSpan[]; wordPattern?: string; } - interface JsxLinkedEditResponse extends Response { + interface LinkedEditingResponse extends Response { readonly body: LinkedEditingRanges; } /** @@ -3858,7 +3858,7 @@ declare namespace ts { private getSemanticDiagnosticsSync; private getSuggestionDiagnosticsSync; private getJsxClosingTag; - private getJsxLinkedEdit; + private getLinkedEditing; private getDocumentHighlights; private provideInlayHints; private setCompilerOptionsForInferredProjects; @@ -9989,7 +9989,7 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; + getLinkedEditingAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; @@ -10019,7 +10019,7 @@ declare namespace ts { interface JsxClosingTagInfo { readonly newText: string; } - interface JsxLinkedEditInfo { + interface LinkedEditingInfo { readonly ranges: TextSpan[]; wordPattern?: string; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c9b3095a92215..21ed21435a4c3 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6107,7 +6107,7 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getJsxLinkedEditAtPosition(fileName: string, position: number): JsxLinkedEditInfo | undefined; + getLinkedEditingAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; @@ -6137,7 +6137,7 @@ declare namespace ts { interface JsxClosingTagInfo { readonly newText: string; } - interface JsxLinkedEditInfo { + interface LinkedEditingInfo { readonly ranges: TextSpan[]; wordPattern?: string; } diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 55dc2573701ab..1244e8031b9ee 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -260,7 +260,7 @@ declare namespace FourSlashInterface { implementationListIsEmpty(): void; isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; - jsxLinkedEdit(map: { [markerName: string]: { + linkedEditing(map: { [markerName: string]: { readonly ranges : { start: number, length: number }[], wordPattern? : string ;} | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/jsxTagLinkedEdit1.ts index f9ff4c0b2805f..8a8656ccb2f88 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit1.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit1.ts @@ -42,7 +42,7 @@ const linkedCursors3 = { wordPattern: 'div' }; -verify.jsxLinkedEdit( { +verify.linkedEditing( { "0": linkedCursors1, "1": linkedCursors1, "2": linkedCursors1, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/jsxTagLinkedEdit2.ts index 4a951630c19e7..1722119a737eb 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit2.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit2.ts @@ -25,7 +25,7 @@ const linkedCursors = { wordPattern : 'div' }; -verify.jsxLinkedEdit( { +verify.linkedEditing( { "0": linkedCursors, "1": linkedCursors, "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/jsxTagLinkedEdit3.ts index c732f45624861..44d08a0e388c5 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit3.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit3.ts @@ -10,7 +10,7 @@ //// /*7*/
////); -verify.jsxLinkedEdit( { +verify.linkedEditing( { "0": undefined, "1": undefined, "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/jsxTagLinkedEdit4.ts index d71e0cec1165a..4126fae1129b7 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit4.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit4.ts @@ -25,7 +25,7 @@ const linkedCursors = { wordPattern : 'div' }; -verify.jsxLinkedEdit( { +verify.linkedEditing( { "0": linkedCursors, "1": linkedCursors, "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit5.ts b/tests/cases/fourslash/jsxTagLinkedEdit5.ts index e6925ff4377f7..9a3254742aaf2 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit5.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit5.ts @@ -14,7 +14,7 @@ const linkedCursors = { wordPattern : 'div' }; -verify.jsxLinkedEdit( { +verify.linkedEditing( { "0": undefined, "1": linkedCursors, "2": linkedCursors, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit6.ts b/tests/cases/fourslash/jsxTagLinkedEdit6.ts index e6231a7dbc9ce..441ae1566d4b2 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit6.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit6.ts @@ -13,7 +13,7 @@ const linkedCursors6 = { wordPattern : 'someNamespace.Thing' }; -verify.jsxLinkedEdit( { +verify.linkedEditing( { "1": linkedCursors6, "2": linkedCursors6, "3": linkedCursors6, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/jsxTagLinkedEdit7.ts index 059ec9e0e0135..7c0262e28a684 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit7.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit7.ts @@ -44,7 +44,7 @@ const linkedCursors2 = { wordPattern : undefined }; -verify.jsxLinkedEdit({ +verify.linkedEditing({ "0": linkedCursors1, "1": undefined, "2": undefined, diff --git a/tests/cases/fourslash/jsxTagLinkedEdit8.ts b/tests/cases/fourslash/jsxTagLinkedEdit8.ts index 4d5584a5a9933..c65d547deedf6 100644 --- a/tests/cases/fourslash/jsxTagLinkedEdit8.ts +++ b/tests/cases/fourslash/jsxTagLinkedEdit8.ts @@ -8,6 +8,6 @@ //// ////); -verify.jsxLinkedEdit( { +verify.linkedEditing( { "8": undefined, }); \ No newline at end of file From a3bdf2e84a30e8c62f0db7fddddae4ed4797d20f Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 31 Mar 2023 14:59:59 -0700 Subject: [PATCH 28/32] rename tests --- .../fourslash/{jsxTagLinkedEdit1.ts => linkedEditingJsxTag1.ts} | 0 .../fourslash/{jsxTagLinkedEdit2.ts => linkedEditingJsxTag2.ts} | 0 .../fourslash/{jsxTagLinkedEdit3.ts => linkedEditingJsxTag3.ts} | 0 .../fourslash/{jsxTagLinkedEdit4.ts => linkedEditingJsxTag4.ts} | 0 .../fourslash/{jsxTagLinkedEdit5.ts => linkedEditingJsxTag5.ts} | 0 .../fourslash/{jsxTagLinkedEdit6.ts => linkedEditingJsxTag6.ts} | 0 .../fourslash/{jsxTagLinkedEdit7.ts => linkedEditingJsxTag7.ts} | 0 .../fourslash/{jsxTagLinkedEdit8.ts => linkedEditingJsxTag8.ts} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename tests/cases/fourslash/{jsxTagLinkedEdit1.ts => linkedEditingJsxTag1.ts} (100%) rename tests/cases/fourslash/{jsxTagLinkedEdit2.ts => linkedEditingJsxTag2.ts} (100%) rename tests/cases/fourslash/{jsxTagLinkedEdit3.ts => linkedEditingJsxTag3.ts} (100%) rename tests/cases/fourslash/{jsxTagLinkedEdit4.ts => linkedEditingJsxTag4.ts} (100%) rename tests/cases/fourslash/{jsxTagLinkedEdit5.ts => linkedEditingJsxTag5.ts} (100%) rename tests/cases/fourslash/{jsxTagLinkedEdit6.ts => linkedEditingJsxTag6.ts} (100%) rename tests/cases/fourslash/{jsxTagLinkedEdit7.ts => linkedEditingJsxTag7.ts} (100%) rename tests/cases/fourslash/{jsxTagLinkedEdit8.ts => linkedEditingJsxTag8.ts} (100%) diff --git a/tests/cases/fourslash/jsxTagLinkedEdit1.ts b/tests/cases/fourslash/linkedEditingJsxTag1.ts similarity index 100% rename from tests/cases/fourslash/jsxTagLinkedEdit1.ts rename to tests/cases/fourslash/linkedEditingJsxTag1.ts diff --git a/tests/cases/fourslash/jsxTagLinkedEdit2.ts b/tests/cases/fourslash/linkedEditingJsxTag2.ts similarity index 100% rename from tests/cases/fourslash/jsxTagLinkedEdit2.ts rename to tests/cases/fourslash/linkedEditingJsxTag2.ts diff --git a/tests/cases/fourslash/jsxTagLinkedEdit3.ts b/tests/cases/fourslash/linkedEditingJsxTag3.ts similarity index 100% rename from tests/cases/fourslash/jsxTagLinkedEdit3.ts rename to tests/cases/fourslash/linkedEditingJsxTag3.ts diff --git a/tests/cases/fourslash/jsxTagLinkedEdit4.ts b/tests/cases/fourslash/linkedEditingJsxTag4.ts similarity index 100% rename from tests/cases/fourslash/jsxTagLinkedEdit4.ts rename to tests/cases/fourslash/linkedEditingJsxTag4.ts diff --git a/tests/cases/fourslash/jsxTagLinkedEdit5.ts b/tests/cases/fourslash/linkedEditingJsxTag5.ts similarity index 100% rename from tests/cases/fourslash/jsxTagLinkedEdit5.ts rename to tests/cases/fourslash/linkedEditingJsxTag5.ts diff --git a/tests/cases/fourslash/jsxTagLinkedEdit6.ts b/tests/cases/fourslash/linkedEditingJsxTag6.ts similarity index 100% rename from tests/cases/fourslash/jsxTagLinkedEdit6.ts rename to tests/cases/fourslash/linkedEditingJsxTag6.ts diff --git a/tests/cases/fourslash/jsxTagLinkedEdit7.ts b/tests/cases/fourslash/linkedEditingJsxTag7.ts similarity index 100% rename from tests/cases/fourslash/jsxTagLinkedEdit7.ts rename to tests/cases/fourslash/linkedEditingJsxTag7.ts diff --git a/tests/cases/fourslash/jsxTagLinkedEdit8.ts b/tests/cases/fourslash/linkedEditingJsxTag8.ts similarity index 100% rename from tests/cases/fourslash/jsxTagLinkedEdit8.ts rename to tests/cases/fourslash/linkedEditingJsxTag8.ts From 228e0ae48d66e9c907fde4a83d9e415e32875f1a Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 3 Apr 2023 01:39:58 -0700 Subject: [PATCH 29/32] more-readable code/comments in getLinkedEditingAtPosition --- src/services/services.ts | 41 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index d3ef99f9bc1cf..65feb87981b38 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2491,20 +2491,19 @@ export function createLanguageService( const openPos = token.parent.parent.openingFragment.getStart(sourceFile) + 1; // "<".length const closePos = token.parent.parent.closingFragment.getStart(sourceFile) + 2; // " + // only allows linked editing right after opening bracket: <| > if ((position !== openPos) && (position !== closePos)) return undefined; - const ranges = [{ start: openPos, length: 0 }, { start: closePos, length: 0 }]; - const wordPattern = undefined; - return { ranges, wordPattern }; + // wordPattern is undefined since fragments have no tag name + return { ranges: [{ start: openPos, length: 0 }, { start: closePos, length: 0 }] }; } else { - // looks for mirroring in element tag names - const tag = findAncestor(token, + // determines if the cursor is in an element tag + const tag = findAncestor(token.parent, n => { - if (!n.parent.parent) return "quit"; - else if (isJsxElement(n.parent.parent)) { - if (isJsxOpeningElement(n.parent) || isJsxClosingElement(n.parent)) { + if (!n.parent) return "quit"; + else if (isJsxElement(n.parent)) { + if (isJsxOpeningElement(n) || isJsxClosingElement(n)) { return true; } return "quit"; @@ -2513,17 +2512,23 @@ export function createLanguageService( }); if (!tag) return undefined; - const element = tag.parent as JsxOpeningElement | JsxClosingElement; - const openTag = { start: element.parent.openingElement.tagName.getStart(sourceFile), end: element.parent.openingElement.tagName.end }; - const endTag = { start: element.parent.closingElement.tagName.getStart(sourceFile), end: element.parent.closingElement.tagName.end }; + const element = tag as JsxOpeningElement | JsxClosingElement; + const openTagStart = element.parent.openingElement.tagName.getStart(sourceFile); + const openTagEnd = element.parent.openingElement.tagName.end; + const closeTagStart = element.parent.closingElement.tagName.getStart(sourceFile); + const closeTagEnd = element.parent.closingElement.tagName.end; - // only return a mirror if the cursor is within a tag name - if (!(openTag.start <= position && position <= openTag.end || endTag.start <= position && position <= endTag.end)) return undefined; - if (element.parent.openingElement.tagName.getText(sourceFile) !== element.parent.closingElement.tagName.getText(sourceFile)) return undefined; + // only return linked cursors if the cursor is within a tag name + if (!(openTagStart <= position && position <= openTagEnd || closeTagStart <= position && position <= closeTagEnd)) return undefined; - const ranges = [{ start: openTag.start, length: openTag.end - openTag.start }, { start: endTag.start, length: endTag.end - endTag.start }]; - const wordPattern = element.tagName.getText(sourceFile); - return { ranges, wordPattern }; + // only return linked cursors if text in both tags is identical + const openingTagText = element.parent.openingElement.tagName.getText(sourceFile); + if (openingTagText !== element.parent.closingElement.tagName.getText(sourceFile)) return undefined; + + return { + ranges: [{ start: openTagStart, length: openTagEnd - openTagStart }, { start: closeTagStart, length: closeTagEnd - closeTagStart }], + wordPattern: openingTagText + }; } } From 32e43c812bcb6278dc81c41380532217f1bea302 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Tue, 4 Apr 2023 15:29:03 -0700 Subject: [PATCH 30/32] add more tests + add `range` to function names --- src/harness/client.ts | 2 +- src/harness/fourslashImpl.ts | 4 +- src/harness/fourslashInterfaceImpl.ts | 2 +- src/harness/harnessLanguageService.ts | 2 +- src/server/protocol.ts | 8 +- src/server/session.ts | 23 +++--- src/services/services.ts | 15 ++-- src/services/types.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 12 +-- tests/baselines/reference/api/typescript.d.ts | 2 +- tests/cases/fourslash/fourslash.ts | 9 ++- tests/cases/fourslash/linkedEditingJsxTag1.ts | 68 +++++++---------- .../cases/fourslash/linkedEditingJsxTag10.ts | 20 +++++ tests/cases/fourslash/linkedEditingJsxTag2.ts | 1 - tests/cases/fourslash/linkedEditingJsxTag3.ts | 22 ++++-- tests/cases/fourslash/linkedEditingJsxTag4.ts | 1 - tests/cases/fourslash/linkedEditingJsxTag5.ts | 47 +++++++++--- tests/cases/fourslash/linkedEditingJsxTag6.ts | 61 +++++++++++++-- tests/cases/fourslash/linkedEditingJsxTag7.ts | 14 ++-- tests/cases/fourslash/linkedEditingJsxTag9.ts | 74 +++++++++++++++++++ 20 files changed, 274 insertions(+), 115 deletions(-) create mode 100644 tests/cases/fourslash/linkedEditingJsxTag10.ts create mode 100644 tests/cases/fourslash/linkedEditingJsxTag9.ts diff --git a/src/harness/client.ts b/src/harness/client.ts index 7c7bdfcb36e54..a74e616072302 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -726,7 +726,7 @@ export class SessionClient implements LanguageService { return notImplemented(); } - getLinkedEditingAtPosition(_fileName: string, _position: number): never { + getLinkedEditingRangeAtPosition(_fileName: string, _position: number): never { return notImplemented(); } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 59437024a0c05..2ee881e653d56 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3444,10 +3444,10 @@ export class TestState { } } - public verifyLinkedEditing(map: { [markerName: string]: ts.LinkedEditingInfo | undefined }): void { + public verifyLinkedEditingRange(map: { [markerName: string]: ts.LinkedEditingInfo | undefined }): void { for (const markerName in map) { this.goToMarker(markerName); - const actual = this.languageService.getLinkedEditingAtPosition(this.activeFile.fileName, this.currentCaretPosition); + const actual = this.languageService.getLinkedEditingRangeAtPosition(this.activeFile.fileName, this.currentCaretPosition); assert.deepEqual(actual, map[markerName], markerName); } } diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index d3306b8b57aa1..ca086dc1b2799 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -190,7 +190,7 @@ export class VerifyNegatable { } public linkedEditing(map: { [markerName: string]: ts.LinkedEditingInfo | undefined }): void { - this.state.verifyLinkedEditing(map); + this.state.verifyLinkedEditingRange(map); } public isInCommentAtPosition(onlyMultiLineDiverges?: boolean) { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index c6f95d66ad4d6..1f1cf4d0abac2 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -593,7 +593,7 @@ class LanguageServiceShimProxy implements ts.LanguageService { getJsxClosingTagAtPosition(): never { throw new Error("Not supported on the shim."); } - getLinkedEditingAtPosition(): never { + getLinkedEditingRangeAtPosition(): never { throw new Error("Not supported on the shim."); } getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): ts.TextSpan { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 3dd2790198078..fa350b88429d0 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -23,7 +23,7 @@ import { export const enum CommandTypes { JsxClosingTag = "jsxClosingTag", - LinkedEditing = "LinkedEditing", + LinkedEditingRange = "linkedEditingRange", Brace = "brace", /** @internal */ BraceFull = "brace-full", @@ -1102,8 +1102,8 @@ export interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } -export interface LinkedEditingRequest extends FileLocationRequest { - readonly command: CommandTypes.LinkedEditing; +export interface LinkedEditingRangeRequest extends FileLocationRequest { + readonly command: CommandTypes.LinkedEditingRange; } export interface LinkedEditingRanges { @@ -1111,7 +1111,7 @@ export interface LinkedEditingRanges { wordPattern?: string; } -export interface LinkedEditingResponse extends Response { +export interface LinkedEditingRangeResponse extends Response { readonly body: LinkedEditingRanges; } diff --git a/src/server/session.ts b/src/server/session.ts index a8269fcee6717..b956f4932e5c4 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1803,10 +1803,10 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } - private getLinkedEditing(args: protocol.FileLocationRequestArgs): protocol.LinkedEditingRanges | undefined { + private getLinkedEditingRange(args: protocol.FileLocationRequestArgs): protocol.LinkedEditingRanges | undefined { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); const position = this.getPositionInFile(args, file); - const linkedEditInfo = languageService.getLinkedEditingAtPosition(file, position); + const linkedEditInfo = languageService.getLinkedEditingRangeAtPosition(file, position); const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); if (scriptInfo === undefined || linkedEditInfo === undefined) return undefined; return convertLinkedEditInfoToRanges(linkedEditInfo, scriptInfo); @@ -3399,8 +3399,8 @@ export class Session implements EventSender { [protocol.CommandTypes.JsxClosingTag]: (request: protocol.JsxClosingTagRequest) => { return this.requiredResponse(this.getJsxClosingTag(request.arguments)); }, - [protocol.CommandTypes.LinkedEditing]: (request: protocol.LinkedEditingRequest) => { - return this.requiredResponse(this.getLinkedEditing(request.arguments)); + [protocol.CommandTypes.LinkedEditingRange]: (request: protocol.LinkedEditingRangeRequest) => { + return this.requiredResponse(this.getLinkedEditingRange(request.arguments)); }, [protocol.CommandTypes.GetCodeFixes]: (request: protocol.CodeFixRequest) => { return this.requiredResponse(this.getCodeFixes(request.arguments, /*simplifiedResult*/ true)); @@ -3658,17 +3658,16 @@ function positionToLineOffset(info: ScriptInfoOrConfig, position: number): proto } function convertLinkedEditInfoToRanges(linkedEdit: LinkedEditingInfo, scriptInfo: ScriptInfo): protocol.LinkedEditingRanges { - return { - ranges: linkedEdit.ranges.map( - s => { + const ranges = linkedEdit.ranges.map( + r => { return { - start: scriptInfo.positionToLineOffset(s.start), - end: scriptInfo.positionToLineOffset(s.start + s.length) + start: scriptInfo.positionToLineOffset(r.start), + end: scriptInfo.positionToLineOffset(r.start + r.length), }; } - ), - wordPattern: linkedEdit.wordPattern, - }; + ); + if (!linkedEdit.wordPattern) return { ranges }; + return { ranges, wordPattern: linkedEdit.wordPattern }; } function locationFromLineAndCharacter(lc: LineAndCharacter): protocol.Location { diff --git a/src/services/services.ts b/src/services/services.ts index 65feb87981b38..d13233dd7d9df 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -186,12 +186,10 @@ import { JSDocTagInfo, JsonSourceFile, JsxAttributes, - JsxClosingElement, JsxClosingTagInfo, JsxElement, JsxEmit, JsxFragment, - JsxOpeningElement, LanguageService, LanguageServiceHost, LanguageServiceMode, @@ -2482,10 +2480,10 @@ export function createLanguageService( } } - function getLinkedEditingAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined { + function getLinkedEditingRangeAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); const token = findPrecedingToken(position, sourceFile); - if (!token) return undefined; + if (!token || token.parent.kind === SyntaxKind.SourceFile) return undefined; if (isJsxFragment(token.parent.parent)) { const openPos = token.parent.parent.openingFragment.getStart(sourceFile) + 1; // "<".length @@ -2494,12 +2492,11 @@ export function createLanguageService( // only allows linked editing right after opening bracket: <| > if ((position !== openPos) && (position !== closePos)) return undefined; - // wordPattern is undefined since fragments have no tag name return { ranges: [{ start: openPos, length: 0 }, { start: closePos, length: 0 }] }; } else { // determines if the cursor is in an element tag - const tag = findAncestor(token.parent, + const element = findAncestor(token.parent, n => { if (!n.parent) return "quit"; else if (isJsxElement(n.parent)) { @@ -2510,9 +2507,8 @@ export function createLanguageService( } return false; }); - if (!tag) return undefined; + if (!element || !(isJsxOpeningElement(element) || isJsxClosingElement(element))) return undefined; - const element = tag as JsxOpeningElement | JsxClosingElement; const openTagStart = element.parent.openingElement.tagName.getStart(sourceFile); const openTagEnd = element.parent.openingElement.tagName.end; const closeTagStart = element.parent.closingElement.tagName.getStart(sourceFile); @@ -2527,7 +2523,6 @@ export function createLanguageService( return { ranges: [{ start: openTagStart, length: openTagEnd - openTagStart }, { start: closeTagStart, length: closeTagEnd - closeTagStart }], - wordPattern: openingTagText }; } } @@ -3063,7 +3058,7 @@ export function createLanguageService( getDocCommentTemplateAtPosition, isValidBraceCompletionAtPosition, getJsxClosingTagAtPosition, - getLinkedEditingAtPosition, + getLinkedEditingRangeAtPosition, getSpanOfEnclosingComment, getCodeFixesAtPosition, getCombinedCodeFix, diff --git a/src/services/types.ts b/src/services/types.ts index 222e2d69e848a..cf3205a88afb0 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -607,7 +607,7 @@ export interface LanguageService { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getLinkedEditingAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; + getLinkedEditingRangeAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 28eb0c067aa12..15c876b40688e 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -89,7 +89,7 @@ declare namespace ts { namespace protocol { enum CommandTypes { JsxClosingTag = "jsxClosingTag", - LinkedEditing = "LinkedEditing", + LinkedEditingRange = "linkedEditingRange", Brace = "brace", BraceCompletion = "braceCompletion", GetSpanOfEnclosingComment = "getSpanOfEnclosingComment", @@ -878,14 +878,14 @@ declare namespace ts { interface JsxClosingTagResponse extends Response { readonly body: TextInsertion; } - interface LinkedEditingRequest extends FileLocationRequest { - readonly command: CommandTypes.LinkedEditing; + interface LinkedEditingRangeRequest extends FileLocationRequest { + readonly command: CommandTypes.LinkedEditingRange; } interface LinkedEditingRanges { ranges: TextSpan[]; wordPattern?: string; } - interface LinkedEditingResponse extends Response { + interface LinkedEditingRangeResponse extends Response { readonly body: LinkedEditingRanges; } /** @@ -3858,7 +3858,7 @@ declare namespace ts { private getSemanticDiagnosticsSync; private getSuggestionDiagnosticsSync; private getJsxClosingTag; - private getLinkedEditing; + private getLinkedEditingRange; private getDocumentHighlights; private provideInlayHints; private setCompilerOptionsForInferredProjects; @@ -9989,7 +9989,7 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getLinkedEditingAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; + getLinkedEditingRangeAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 21ed21435a4c3..bff422b6466da 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -6107,7 +6107,7 @@ declare namespace ts { * Editors should call this after `>` is typed. */ getJsxClosingTagAtPosition(fileName: string, position: number): JsxClosingTagInfo | undefined; - getLinkedEditingAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; + getLinkedEditingRangeAtPosition(fileName: string, position: number): LinkedEditingInfo | undefined; getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): TextSpan | undefined; toLineColumnOffset?(fileName: string, position: number): LineAndCharacter; getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: readonly number[], formatOptions: FormatCodeSettings, preferences: UserPreferences): readonly CodeFixAction[]; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 1244e8031b9ee..4ad2445b60cd9 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -260,9 +260,7 @@ declare namespace FourSlashInterface { implementationListIsEmpty(): void; isValidBraceCompletionAtPosition(openingBrace?: string): void; jsxClosingTag(map: { [markerName: string]: { readonly newText: string } | undefined }): void; - linkedEditing(map: { [markerName: string]: { - readonly ranges : { start: number, length: number }[], - wordPattern? : string ;} | undefined }): void; + linkedEditing(map: { [markerName: string]: LinkedEditingInfo | undefined }): void; isInCommentAtPosition(onlyMultiLineDiverges?: boolean): void; codeFix(options: { description: string | [string, ...(string | number)[]] | DiagnosticIgnoredInterpolations, @@ -762,6 +760,11 @@ declare namespace FourSlashInterface { generateReturnInDocTemplate?: boolean; } + type LinkedEditingInfo = { + readonly ranges : { start: number, length: number }[]; + wordPattern? : string; + } + export type SignatureHelpTriggerReason = | SignatureHelpInvokedReason | SignatureHelpCharacterTypedReason diff --git a/tests/cases/fourslash/linkedEditingJsxTag1.ts b/tests/cases/fourslash/linkedEditingJsxTag1.ts index 8a8656ccb2f88..ab0183c9c1e15 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag1.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag1.ts @@ -7,39 +7,30 @@ //); // @Filename: /basic.tsx -////const jsx = ( -//// /*3*/ +/////*a*/const j/*b*/sx = ( +//// /*c*//*3*/ //// /*8*/ ////); +////const jsx2 = ( +//// +//// +////

+//// +//// +//// +////);/*d*/ -// @Filename: /whitespaceInvalidClosing.tsx -////const jsx = ( -////

-//// < /*9*/ /div> -////); - -// @Filename: /whitespace.tsx -////const whitespaceOpening = ( -//// /*14*/ -//// -////); -////const whitespaceClosing = ( -//// -//// /*23*/ -////); - -const markers = test.markers(); const linkedCursors1 = { - ranges: [{ start: markers[0].position, length: 3 }, { start: markers[5].position, length: 3 }], - wordPattern: 'div' + ranges: [{ start: test.markerByName("0").position, length: 3 }, { start: test.markerByName("5").position, length: 3 }], }; const linkedCursors2 = { - ranges: [{ start: markers[11].position, length: 3 }, { start: markers[15].position, length: 3 }], - wordPattern: 'div' + ranges: [{ start: test.markerByName("9").position - 1, length: 3 }, { start: test.markerByName("14").position - 1, length: 3 }], }; const linkedCursors3 = { - ranges: [{ start: markers[17].position, length: 3 }, { start: markers[20].position, length: 3 }], - wordPattern: 'div' + ranges: [{ start: test.markerByName("10").position - 1, length: 3 }, { start: test.markerByName("13").position - 1, length: 3 }], +}; +const linkedCursors4 = { + ranges: [{ start: test.markerByName("11").position - 1, length: 1 }, { start: test.markerByName("12").position, length: 1 }], }; verify.linkedEditing( { @@ -52,20 +43,15 @@ verify.linkedEditing( { "6": linkedCursors1, "7": linkedCursors1, "8": undefined, - "9": undefined, // I believe this should be an invalid tag - "10": undefined, - "11": linkedCursors2, - "12": linkedCursors2, - "13": undefined, - "14": undefined, - "15": linkedCursors2, - "16": linkedCursors2, - "17": linkedCursors3, - "18": linkedCursors3, - "19": undefined, - "20": linkedCursors3, - "21": linkedCursors3, - "22": undefined, - "23": undefined, - }); + "9": linkedCursors2, + "10": linkedCursors3, + "11": linkedCursors4, + "12": linkedCursors4, + "13": linkedCursors3, + "14": linkedCursors2, + "a": undefined, + "b": undefined, + "c": undefined, + "d": undefined, +}); diff --git a/tests/cases/fourslash/linkedEditingJsxTag10.ts b/tests/cases/fourslash/linkedEditingJsxTag10.ts new file mode 100644 index 0000000000000..12a4255801d49 --- /dev/null +++ b/tests/cases/fourslash/linkedEditingJsxTag10.ts @@ -0,0 +1,20 @@ +/// + +// @Filename: /jsx1.tsx +////const jsx = ; + +// @Filename: /jsx2.tsx +////const jsx = ; + +// const startPos = test.markerByName("start").position; +// const endPos = test.markerByName("end").position; +// const linkedCursors6 = { +// ranges: [{ start: startPos, length: 19 }, { start: endPos, length: 19 }], +// }; + +verify.linkedEditing( { + "0": undefined, + "1": undefined, + // "2": undefined, these cases don't work yet + // "3": undefined, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/linkedEditingJsxTag2.ts b/tests/cases/fourslash/linkedEditingJsxTag2.ts index 1722119a737eb..e4d9abfb5bbec 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag2.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag2.ts @@ -22,7 +22,6 @@ const startPos = test.markerByName("0").position; const endPos = test.markerByName("6").position - 2; const linkedCursors = { ranges: [{ start: startPos, length: 3 }, { start: endPos, length: 3 }], - wordPattern : 'div' }; verify.linkedEditing( { diff --git a/tests/cases/fourslash/linkedEditingJsxTag3.ts b/tests/cases/fourslash/linkedEditingJsxTag3.ts index 44d08a0e388c5..2dfaa175e4cf9 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag3.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag3.ts @@ -1,14 +1,14 @@ /// // @Filename: /selfClosing.tsx -////const jsx = ( -////
/*0*/ -////

/*4*/ -//// No lin/*9*/ked cursors here! -//// /*5*/ -//// /*6*/

/*8*/ -//// /*7*/
-////); +/////*0*/const jsx = /*1*/( +////
/*2*/ +////

/*3*/ +//// No lin/*4*/ked cursors here! +//// /*5*/ +//// /*10*/

/*11*/ +//// /*12*/
+/////*13*/)/*14*/;/*15*/ verify.linkedEditing( { "0": undefined, @@ -21,4 +21,10 @@ verify.linkedEditing( { "7": undefined, "8": undefined, "9": undefined, + "10": undefined, + "11": undefined, + "12": undefined, + "13": undefined, + "14": undefined, + "15": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/linkedEditingJsxTag4.ts b/tests/cases/fourslash/linkedEditingJsxTag4.ts index 4126fae1129b7..2b95b156eb049 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag4.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag4.ts @@ -22,7 +22,6 @@ const startPos = test.markerByName("0").position; const endPos = test.markerByName("6").position; const linkedCursors = { ranges: [{ start: startPos, length: 3 }, { start: endPos, length: 3 }], - wordPattern : 'div' }; verify.linkedEditing( { diff --git a/tests/cases/fourslash/linkedEditingJsxTag5.ts b/tests/cases/fourslash/linkedEditingJsxTag5.ts index 9a3254742aaf2..2e7dbe56acea2 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag5.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag5.ts @@ -1,21 +1,50 @@ /// -// @FileName: /invalid1.tsx +// @FileName: /unclosedElement.tsx ////const jsx = ( ////
////
-////
+////
/*3*/ +////);/*4*/ + +// @FileName: /mismatchedElement.tsx +////const jsx = ( +//// /*5*/
+////
+//// /*10*/ ////); -const startPos = test.markerByName("1").position - 3; -const endPos = test.markerByName("2").position - 3; -const linkedCursors = { - ranges: [{ start: startPos, length: 3 }, { start: endPos, length: 3 }], - wordPattern : 'div' +// @Filename: /invalidClosing.tsx +////const jsx = ( +//// +//// +////); + +const startPos1 = test.markerByName("1").position - 3; +const endPos1 = test.markerByName("2").position - 3; +const linkedCursors1 = { + ranges: [{ start: startPos1, length: 3 }, { start: endPos1, length: 3 }], +}; + +const startPos2 = test.markerByName("6").position - 3; +const endPos2 = test.markerByName("7").position - 3; +const linkedCursors2 = { + ranges: [{ start: startPos2, length: 3 }, { start: endPos2, length: 3 }], }; verify.linkedEditing( { "0": undefined, - "1": linkedCursors, - "2": linkedCursors, + "1": linkedCursors1, + "2": linkedCursors1, + "3": undefined, + "4": undefined, + "5": undefined, + "6": linkedCursors2, + "7": linkedCursors2, + "8": undefined, + "9": undefined, + "10": undefined, + "11": undefined, // this tag does not parse as a closing tag + "12": undefined, + "13": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/linkedEditingJsxTag6.ts b/tests/cases/fourslash/linkedEditingJsxTag6.ts index 441ae1566d4b2..8cac987be18cb 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag6.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag6.ts @@ -5,16 +5,61 @@ //// //// ////); +//// const jsx1 = ; +//// const jsx2 = ; +//// const jsx3 = +//// ; +//// let jsx4 = +//// +//// -const startPos = test.markerByName("start").position; -const endPos = test.markerByName("end").position; -const linkedCursors6 = { - ranges: [{ start: startPos, length: 19 }, { start: endPos, length: 19 }], - wordPattern : 'someNamespace.Thing' +const startPos1 = test.markerByName("start").position; +const endPos1 = test.markerByName("end").position; +const linkedCursors1 = { + ranges: [{ start: startPos1, length: 19 }, { start: endPos1, length: 19 }], +}; + +const startPos2 = test.markerByName("26").position; +const endPos2 = test.markerByName("30").position; +const linkedCursors2 = { + ranges: [{ start: startPos2, length: 21 }, { start: endPos2, length: 21 }], }; verify.linkedEditing( { - "1": linkedCursors6, - "2": linkedCursors6, - "3": linkedCursors6, + "1": linkedCursors1, + "2": linkedCursors1, + "3": linkedCursors1, + "4": undefined, + "5": undefined, + "6": undefined, + "7": undefined, + "8": undefined, + "9": undefined, + "10": undefined, + "11": undefined, + "12": undefined, + "13": undefined, + "14": undefined, + "15": undefined, + "16": undefined, + "17": undefined, + "18": undefined, + "19": undefined, + "20": undefined, + "21": undefined, + "22": undefined, + "23": undefined, + "24": undefined, + "25": undefined, + "26": linkedCursors2, + "27": linkedCursors2, + "28": linkedCursors2, + "29": linkedCursors2, + "30": linkedCursors2, + "31": linkedCursors2, + "32": linkedCursors2, + "33": linkedCursors2, }); \ No newline at end of file diff --git a/tests/cases/fourslash/linkedEditingJsxTag7.ts b/tests/cases/fourslash/linkedEditingJsxTag7.ts index 7c0262e28a684..c401de04cb5d0 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag7.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag7.ts @@ -16,11 +16,11 @@ // ); // @FileName: /fragment.tsx -////const jsx = ( +/////*a*/const j/*b*/sx =/*c*/ ( //// /*5*//*1*/ //// //// /*6*//*4*/ -////); +////)/*d*/; ////const jsx2 = ( //// /* this is comment *//*13*//*8*/Hello/*9*/ //// @@ -28,20 +28,19 @@ ////const jsx3 = ( //// <>/*7*/ //// -////); +////);/*e*/ const startPos1 = test.markerByName("0").position; const endPos1 = test.markerByName("3").position; const linkedCursors1 = { ranges: [{ start: startPos1, length: 0 }, { start: endPos1, length: 0 }], - wordPattern : undefined + // wordPattern : undefined }; const startPos2 = test.markerByName("10").position; const endPos2 = test.markerByName("14").position; const linkedCursors2 = { ranges: [{ start: startPos2, length: 0 }, { start: endPos2, length: 0 }], - wordPattern : undefined }; verify.linkedEditing({ @@ -64,4 +63,9 @@ verify.linkedEditing({ "16": undefined, "17": undefined, "18": undefined, + "a": undefined, + "b": undefined, + "c": undefined, + "d": undefined, + "e": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/linkedEditingJsxTag9.ts b/tests/cases/fourslash/linkedEditingJsxTag9.ts new file mode 100644 index 0000000000000..67bd28d1fc5e7 --- /dev/null +++ b/tests/cases/fourslash/linkedEditingJsxTag9.ts @@ -0,0 +1,74 @@ +/// + +// the content of whitespace.tsx +//const whitespaceOpening = ( +// < div > +//
+//); +//const whitespaceClosing = ( +//
+// +//); +//const triviaOpening = ( +// /* this is comment */Hello +// +//); + +// @Filename: /whitespace.tsx +////const whitespaceOpening = ( +//// /*4*/ +//// +////); +////const whitespaceClosing = ( +//// +//// /*13*/ +////); +////const triviaOpening = ( +//// /* this is/*14*/ comment *//*15*//*21*/Hello/*22*/ +//// +////); + +const markers = test.markers(); +const linkedCursors1 = { + ranges: [{ start: markers[1].position, length: 3 }, { start: markers[5].position, length: 3 }], +}; +const linkedCursors2 = { + ranges: [{ start: markers[7].position, length: 3 }, { start: markers[10].position, length: 3 }], +}; +const linkedCursors3 = { + ranges: [{ start: markers[20].position - 2, length: 3 }, { start: markers[28].position - 1, length: 3 }], +}; + +verify.linkedEditing( { + "0": undefined, + "1": linkedCursors1, + "2": linkedCursors1, + "3": undefined, + "4": undefined, + "5": linkedCursors1, + "6": linkedCursors1, + "7": linkedCursors2, + "8": linkedCursors2, + "9": undefined, + "10": linkedCursors2, + "11": linkedCursors2, + "12": undefined, + "13": undefined, + "14": undefined, + "15": undefined, + "16": undefined, + "17": undefined, + "18": undefined, + "19": undefined, + "20": linkedCursors3, + "21": undefined, + "22": undefined, + "23": undefined, + "24": undefined, + "25": undefined, + "26": undefined, + "27": undefined, + "28": linkedCursors3, + "29": undefined +}); + From da4f1cd2ec1bd64bb287282fac36422bebc98304 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Thu, 6 Apr 2023 14:15:09 -0700 Subject: [PATCH 31/32] more tests --- src/services/services.ts | 9 +- .../cases/fourslash/linkedEditingJsxTag10.ts | 86 +++++++++++++++++-- tests/cases/fourslash/linkedEditingJsxTag2.ts | 15 ++++ tests/cases/fourslash/linkedEditingJsxTag4.ts | 14 +++ 4 files changed, 113 insertions(+), 11 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index d13233dd7d9df..50bf8c419db2c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -34,6 +34,7 @@ import { Completions, computePositionOfLineAndCharacter, computeSuggestionDiagnostics, + containsParseError, createDocumentRegistry, createGetCanonicalFileName, createMultiMap, @@ -2486,8 +2487,12 @@ export function createLanguageService( if (!token || token.parent.kind === SyntaxKind.SourceFile) return undefined; if (isJsxFragment(token.parent.parent)) { - const openPos = token.parent.parent.openingFragment.getStart(sourceFile) + 1; // "<".length - const closePos = token.parent.parent.closingFragment.getStart(sourceFile) + 2; // " if ((position !== openPos) && (position !== closePos)) return undefined; diff --git a/tests/cases/fourslash/linkedEditingJsxTag10.ts b/tests/cases/fourslash/linkedEditingJsxTag10.ts index 12a4255801d49..0cc40d35da978 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag10.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag10.ts @@ -1,20 +1,88 @@ /// +// @Filename: /jsx0.tsx +////const jsx = + // @Filename: /jsx1.tsx -////const jsx = ; +////const jsx = // @Filename: /jsx2.tsx -////const jsx = ; +////const jsx = + +// @Filename: /jsx3.tsx +////const jsx = + +// @Filename: /jsx4.tsx +////const jsx = ; + +// @Filename: /jsx5.tsx +////const jsx = ; + +// @Filename: /jsx6.tsx +////const jsx = /*6*/div> ; + +// @Filename: /jsx7.tsx +////const jsx = //*7a*/div>; + +// @Filename: /jsx8.tsx +////const jsx = ; + +// @Filename: /jsx9.tsx +////const jsx = ; + +// @Filename: /jsx12.tsx +////const jsx = /*12*/> ; + +// @Filename: /jsx13.tsx +////const jsx = //*13a*/>; + +// @Filename: /jsx14.tsx +////const jsx = ; + +// @Filename: /jsx15.tsx +////const jsx = ; -// const startPos = test.markerByName("start").position; -// const endPos = test.markerByName("end").position; -// const linkedCursors6 = { -// ranges: [{ start: startPos, length: 19 }, { start: endPos, length: 19 }], -// }; +const linkedCursors9 = { + ranges: [{ start: test.markerByName("9").position, length: 3 }, { start: test.markerByName("9a").position, length: 3 }], +}; verify.linkedEditing( { "0": undefined, "1": undefined, - // "2": undefined, these cases don't work yet - // "3": 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 diff --git a/tests/cases/fourslash/linkedEditingJsxTag2.ts b/tests/cases/fourslash/linkedEditingJsxTag2.ts index e4d9abfb5bbec..35c5a119653c9 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag2.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag2.ts @@ -18,6 +18,14 @@ //// ////); +// this case is missing a closing brace in the attributes +// @Filename: /attrsError.tsx +////const jsx = ( +//// /*15*/ +//// +//// +////); + const startPos = test.markerByName("0").position; const endPos = test.markerByName("6").position - 2; const linkedCursors = { @@ -32,4 +40,11 @@ verify.linkedEditing( { "4": undefined, "5": undefined, "6": linkedCursors, + "10": undefined, + "11": undefined, + "12": undefined, + "13": undefined, + "14": undefined, + "15": undefined, + "16": undefined, }); \ No newline at end of file diff --git a/tests/cases/fourslash/linkedEditingJsxTag4.ts b/tests/cases/fourslash/linkedEditingJsxTag4.ts index 2b95b156eb049..b130c55e024f9 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag4.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag4.ts @@ -18,6 +18,13 @@ //// ////); +// @Filename: /typeTagError.tsx +////const jsx = ( +//// /*14*/ +//// +//// +////); + const startPos = test.markerByName("0").position; const endPos = test.markerByName("6").position; const linkedCursors = { @@ -32,4 +39,11 @@ verify.linkedEditing( { "4": undefined, "5": undefined, "6": linkedCursors, + "10": undefined, + "11": undefined, + "12": undefined, + "13": undefined, + "14": undefined, + "15": undefined, + "16": undefined, }); \ No newline at end of file From db67aeedd5b3f9be3e19d37386ba5d34507d91c2 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Fri, 7 Apr 2023 12:21:58 -0700 Subject: [PATCH 32/32] update `Body`, simplify call to `findAncestor` --- src/server/protocol.ts | 4 +-- src/server/session.ts | 4 +-- src/services/services.ts | 28 +++++++++---------- .../reference/api/tsserverlibrary.d.ts | 4 +-- tests/cases/fourslash/fourslash.ts | 4 +-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index fa350b88429d0..fbd7f04088ae4 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1106,13 +1106,13 @@ export interface LinkedEditingRangeRequest extends FileLocationRequest { readonly command: CommandTypes.LinkedEditingRange; } -export interface LinkedEditingRanges { +export interface LinkedEditingRangesBody { ranges: TextSpan[]; wordPattern?: string; } export interface LinkedEditingRangeResponse extends Response { - readonly body: LinkedEditingRanges; + readonly body: LinkedEditingRangesBody; } /** diff --git a/src/server/session.ts b/src/server/session.ts index b956f4932e5c4..853bfadc38882 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1803,7 +1803,7 @@ export class Session implements EventSender { return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 }; } - private getLinkedEditingRange(args: protocol.FileLocationRequestArgs): protocol.LinkedEditingRanges | undefined { + private getLinkedEditingRange(args: protocol.FileLocationRequestArgs): protocol.LinkedEditingRangesBody | undefined { const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args); const position = this.getPositionInFile(args, file); const linkedEditInfo = languageService.getLinkedEditingRangeAtPosition(file, position); @@ -3657,7 +3657,7 @@ function positionToLineOffset(info: ScriptInfoOrConfig, position: number): proto return isConfigFile(info) ? locationFromLineAndCharacter(info.getLineAndCharacterOfPosition(position)) : info.positionToLineOffset(position); } -function convertLinkedEditInfoToRanges(linkedEdit: LinkedEditingInfo, scriptInfo: ScriptInfo): protocol.LinkedEditingRanges { +function convertLinkedEditInfoToRanges(linkedEdit: LinkedEditingInfo, scriptInfo: ScriptInfo): protocol.LinkedEditingRangesBody { const ranges = linkedEdit.ranges.map( r => { return { diff --git a/src/services/services.ts b/src/services/services.ts index 50bf8c419db2c..938f946a0d614 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2501,30 +2501,30 @@ export function createLanguageService( } else { // determines if the cursor is in an element tag - const element = findAncestor(token.parent, + const tag = findAncestor(token.parent, n => { - if (!n.parent) return "quit"; - else if (isJsxElement(n.parent)) { - if (isJsxOpeningElement(n) || isJsxClosingElement(n)) { - return true; - } - return "quit"; + if (isJsxOpeningElement(n) || isJsxClosingElement(n)) { + return true; } return false; }); - if (!element || !(isJsxOpeningElement(element) || isJsxClosingElement(element))) return undefined; + if (!tag) return undefined; + Debug.assert(isJsxOpeningElement(tag) || isJsxClosingElement(tag), "tag should be opening or closing element"); + + const openTag = tag.parent.openingElement; + const closeTag = tag.parent.closingElement; - const openTagStart = element.parent.openingElement.tagName.getStart(sourceFile); - const openTagEnd = element.parent.openingElement.tagName.end; - const closeTagStart = element.parent.closingElement.tagName.getStart(sourceFile); - const closeTagEnd = element.parent.closingElement.tagName.end; + const openTagStart = openTag.tagName.getStart(sourceFile); + const openTagEnd = openTag.tagName.end; + const closeTagStart = closeTag.tagName.getStart(sourceFile); + const closeTagEnd = closeTag.tagName.end; // only return linked cursors if the cursor is within a tag name if (!(openTagStart <= position && position <= openTagEnd || closeTagStart <= position && position <= closeTagEnd)) return undefined; // only return linked cursors if text in both tags is identical - const openingTagText = element.parent.openingElement.tagName.getText(sourceFile); - if (openingTagText !== element.parent.closingElement.tagName.getText(sourceFile)) return undefined; + const openingTagText = openTag.tagName.getText(sourceFile); + if (openingTagText !== closeTag.tagName.getText(sourceFile)) return undefined; return { ranges: [{ start: openTagStart, length: openTagEnd - openTagStart }, { start: closeTagStart, length: closeTagEnd - closeTagStart }], diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 15c876b40688e..c60b924ffb3e0 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -881,12 +881,12 @@ declare namespace ts { interface LinkedEditingRangeRequest extends FileLocationRequest { readonly command: CommandTypes.LinkedEditingRange; } - interface LinkedEditingRanges { + interface LinkedEditingRangesBody { ranges: TextSpan[]; wordPattern?: string; } interface LinkedEditingRangeResponse extends Response { - readonly body: LinkedEditingRanges; + readonly body: LinkedEditingRangesBody; } /** * Get document highlights request; value of command field is diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 4ad2445b60cd9..51a0eca5b1a18 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -761,8 +761,8 @@ declare namespace FourSlashInterface { } type LinkedEditingInfo = { - readonly ranges : { start: number, length: number }[]; - wordPattern? : string; + readonly ranges: { start: number, length: number }[]; + wordPattern?: string; } export type SignatureHelpTriggerReason =