From 220ff1abcee5fc3ab39a3fc0e96d7b34753fa6a6 Mon Sep 17 00:00:00 2001 From: Isabel Duan <isabelduan@microsoft.com> Date: Wed, 19 Apr 2023 13:01:51 -0700 Subject: [PATCH 1/2] added `wordPattern` Regex to include more characters --- src/services/services.ts | 9 +++++- tests/cases/fourslash/linkedEditingJsxTag1.ts | 7 ++++ .../cases/fourslash/linkedEditingJsxTag10.ts | 2 ++ .../cases/fourslash/linkedEditingJsxTag11.ts | 32 +++++++++++++++++++ tests/cases/fourslash/linkedEditingJsxTag2.ts | 2 ++ tests/cases/fourslash/linkedEditingJsxTag4.ts | 2 ++ tests/cases/fourslash/linkedEditingJsxTag5.ts | 4 +++ tests/cases/fourslash/linkedEditingJsxTag6.ts | 4 +++ tests/cases/fourslash/linkedEditingJsxTag7.ts | 5 ++- tests/cases/fourslash/linkedEditingJsxTag9.ts | 5 +++ 10 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/linkedEditingJsxTag11.ts diff --git a/src/services/services.ts b/src/services/services.ts index acd845c7979c6..cb2c73939bdd7 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2492,6 +2492,9 @@ export function createLanguageService( const token = findPrecedingToken(position, sourceFile); if (!token || token.parent.kind === SyntaxKind.SourceFile) return undefined; + // matches more than valid tag names to allow linked editing when typing is in progress or tag name is incomplete + const jsxTagWordPattern = "[a-zA-Z0-9:\\-\\._$]*"; + if (isJsxFragment(token.parent.parent)) { const openFragment = token.parent.parent.openingFragment; const closeFragment = token.parent.parent.closingFragment; @@ -2503,7 +2506,10 @@ export function createLanguageService( // only allows linked editing right after opening bracket: <| ></| > if ((position !== openPos) && (position !== closePos)) return undefined; - return { ranges: [{ start: openPos, length: 0 }, { start: closePos, length: 0 }] }; + return { + ranges: [{ start: openPos, length: 0 }, { start: closePos, length: 0 }], + wordPattern: jsxTagWordPattern, + }; } else { // determines if the cursor is in an element tag @@ -2534,6 +2540,7 @@ export function createLanguageService( return { ranges: [{ start: openTagStart, length: openTagEnd - openTagStart }, { start: closeTagStart, length: closeTagEnd - closeTagStart }], + wordPattern: jsxTagWordPattern, }; } } diff --git a/tests/cases/fourslash/linkedEditingJsxTag1.ts b/tests/cases/fourslash/linkedEditingJsxTag1.ts index ab0183c9c1e15..2fe2624231d63 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag1.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag1.ts @@ -20,17 +20,24 @@ //// </d/*14*/iv> ////);/*d*/ +// goTo.marker("a"); + +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; const linkedCursors1 = { ranges: [{ start: test.markerByName("0").position, length: 3 }, { start: test.markerByName("5").position, length: 3 }], + wordPattern, }; const linkedCursors2 = { ranges: [{ start: test.markerByName("9").position - 1, length: 3 }, { start: test.markerByName("14").position - 1, length: 3 }], + wordPattern, }; const linkedCursors3 = { ranges: [{ start: test.markerByName("10").position - 1, length: 3 }, { start: test.markerByName("13").position - 1, length: 3 }], + wordPattern, }; const linkedCursors4 = { ranges: [{ start: test.markerByName("11").position - 1, length: 1 }, { start: test.markerByName("12").position, length: 1 }], + wordPattern, }; verify.linkedEditing( { diff --git a/tests/cases/fourslash/linkedEditingJsxTag10.ts b/tests/cases/fourslash/linkedEditingJsxTag10.ts index 0cc40d35da978..509f858010e7e 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag10.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag10.ts @@ -48,8 +48,10 @@ // @Filename: /jsx15.tsx ////const jsx = </*15*/div> </*15a*/> <//*15b*/div> <//*15c*/>; +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; const linkedCursors9 = { ranges: [{ start: test.markerByName("9").position, length: 3 }, { start: test.markerByName("9a").position, length: 3 }], + wordPattern, }; verify.linkedEditing( { diff --git a/tests/cases/fourslash/linkedEditingJsxTag11.ts b/tests/cases/fourslash/linkedEditingJsxTag11.ts new file mode 100644 index 0000000000000..668c1bb2692b8 --- /dev/null +++ b/tests/cases/fourslash/linkedEditingJsxTag11.ts @@ -0,0 +1,32 @@ +/// <reference path='fourslash.ts' /> + +// for readability +////const jsx = ( +//// <div style={{ color: 'red' }}> +//// <p> +//// <img /> +//// </p> +//// </div> +////); + +// @Filename: /attrs.tsx +//// </*1*/fbt/*2*/:en/*3*/um knownProp="accepted" +//// unknownProp="rejected"> +//// </f/*4*/bt:e/*5*/num>; + + +const startPos = test.markerByName("1").position; +const endPos = test.markerByName("4").position - 1; +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; +const linkedCursors = { + ranges: [{ start: startPos, length: 8 }, { start: endPos, length: 8 }], + wordPattern, +}; + +verify.linkedEditing( { + "1": linkedCursors, + "2": linkedCursors, + "3": linkedCursors, + "4": linkedCursors, + "5": linkedCursors, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/linkedEditingJsxTag2.ts b/tests/cases/fourslash/linkedEditingJsxTag2.ts index 35c5a119653c9..d16821137bc96 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag2.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag2.ts @@ -28,8 +28,10 @@ const startPos = test.markerByName("0").position; const endPos = test.markerByName("6").position - 2; +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; const linkedCursors = { ranges: [{ start: startPos, length: 3 }, { start: endPos, length: 3 }], + wordPattern, }; verify.linkedEditing( { diff --git a/tests/cases/fourslash/linkedEditingJsxTag4.ts b/tests/cases/fourslash/linkedEditingJsxTag4.ts index b130c55e024f9..bd4e9f7179119 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag4.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag4.ts @@ -27,8 +27,10 @@ const startPos = test.markerByName("0").position; const endPos = test.markerByName("6").position; +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; const linkedCursors = { ranges: [{ start: startPos, length: 3 }, { start: endPos, length: 3 }], + wordPattern, }; verify.linkedEditing( { diff --git a/tests/cases/fourslash/linkedEditingJsxTag5.ts b/tests/cases/fourslash/linkedEditingJsxTag5.ts index 2e7dbe56acea2..87379e19a3e4b 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag5.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag5.ts @@ -20,16 +20,20 @@ //// </*12*/ //*13*/div> ////); +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; + 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 }], + wordPattern, }; 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 }], + wordPattern, }; verify.linkedEditing( { diff --git a/tests/cases/fourslash/linkedEditingJsxTag6.ts b/tests/cases/fourslash/linkedEditingJsxTag6.ts index 8cac987be18cb..482fec57bb2a7 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag6.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag6.ts @@ -16,16 +16,20 @@ //// <//*30*/foo /*31*/ .// hi/*32*/ //// /*33*/bar> +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; + const startPos1 = test.markerByName("start").position; const endPos1 = test.markerByName("end").position; const linkedCursors1 = { ranges: [{ start: startPos1, length: 19 }, { start: endPos1, length: 19 }], + wordPattern, }; const startPos2 = test.markerByName("26").position; const endPos2 = test.markerByName("30").position; const linkedCursors2 = { ranges: [{ start: startPos2, length: 21 }, { start: endPos2, length: 21 }], + wordPattern, }; verify.linkedEditing( { diff --git a/tests/cases/fourslash/linkedEditingJsxTag7.ts b/tests/cases/fourslash/linkedEditingJsxTag7.ts index c401de04cb5d0..9a746c04f0fbd 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag7.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag7.ts @@ -30,17 +30,20 @@ //// </> ////);/*e*/ +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; + 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, }; const startPos2 = test.markerByName("10").position; const endPos2 = test.markerByName("14").position; const linkedCursors2 = { ranges: [{ start: startPos2, length: 0 }, { start: endPos2, length: 0 }], + wordPattern, }; verify.linkedEditing({ diff --git a/tests/cases/fourslash/linkedEditingJsxTag9.ts b/tests/cases/fourslash/linkedEditingJsxTag9.ts index 67bd28d1fc5e7..7f363a8ed619f 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag9.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag9.ts @@ -28,15 +28,20 @@ //// <//*23*/ /*24*///*25*/* even/*26*/ more comment *//*27*/ d/*28*/iv /* b/*29*/ye */> ////); +const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; + const markers = test.markers(); const linkedCursors1 = { ranges: [{ start: markers[1].position, length: 3 }, { start: markers[5].position, length: 3 }], + wordPattern, }; const linkedCursors2 = { ranges: [{ start: markers[7].position, length: 3 }, { start: markers[10].position, length: 3 }], + wordPattern, }; const linkedCursors3 = { ranges: [{ start: markers[20].position - 2, length: 3 }, { start: markers[28].position - 1, length: 3 }], + wordPattern, }; verify.linkedEditing( { From 3d460f66afdd768a111fd8f990222385566fe944 Mon Sep 17 00:00:00 2001 From: Isabel Duan <isabelduan@microsoft.com> Date: Thu, 20 Apr 2023 13:28:43 -0700 Subject: [PATCH 2/2] add tests --- .../cases/fourslash/linkedEditingJsxTag11.ts | 53 ++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/tests/cases/fourslash/linkedEditingJsxTag11.ts b/tests/cases/fourslash/linkedEditingJsxTag11.ts index 668c1bb2692b8..3755f78a8cb0b 100644 --- a/tests/cases/fourslash/linkedEditingJsxTag11.ts +++ b/tests/cases/fourslash/linkedEditingJsxTag11.ts @@ -9,24 +9,53 @@ //// </div> ////); -// @Filename: /attrs.tsx -//// </*1*/fbt/*2*/:en/*3*/um knownProp="accepted" +// @Filename: /customElements.tsx +//// const jsx = </*1*/fbt/*2*/:en/*3*/um knownProp="accepted" //// unknownProp="rejected"> //// </f/*4*/bt:e/*5*/num>; +//// +//// const customElement = </*6*/custom/*7*/-element/*8*/></custom/*9*/-element>; +//// +//// const standardElement = +//// </*10*/Link/*11*/ href="/hello" passHref> +//// </*12*/But/*13*/ton component="a"> +//// Next +//// </But/*14*/ton> +//// </Li/*15*/nk>; - -const startPos = test.markerByName("1").position; -const endPos = test.markerByName("4").position - 1; const wordPattern = "[a-zA-Z0-9:\\-\\._$]*"; -const linkedCursors = { - ranges: [{ start: startPos, length: 8 }, { start: endPos, length: 8 }], + +const linkedCursors1 = { + ranges: [{ start: test.markerByName("1").position, length: 8 }, { start: test.markerByName("4").position - 1, length: 8 }], + wordPattern, +}; +const linkedCursors2 = { + ranges: [{ start: test.markerByName("6").position, length: 14 }, { start: test.markerByName("9").position - 6, length: 14 }], + wordPattern, +}; +const linkedCursors3 = { + ranges: [{ start: test.markerByName("10").position, length: 4 }, { start: test.markerByName("15").position - 2, length: 4 }], + wordPattern, +}; +const linkedCursors4 = { + ranges: [{ start: test.markerByName("12").position, length: 6 }, { start: test.markerByName("14").position - 3, length: 6 }], wordPattern, }; verify.linkedEditing( { - "1": linkedCursors, - "2": linkedCursors, - "3": linkedCursors, - "4": linkedCursors, - "5": linkedCursors, + "1": linkedCursors1, + "2": linkedCursors1, + "3": linkedCursors1, + "4": linkedCursors1, + "5": linkedCursors1, + "6": linkedCursors2, + "7": linkedCursors2, + "8": linkedCursors2, + "9": linkedCursors2, + "10": linkedCursors3, + "11": linkedCursors3, + "12": linkedCursors4, + "13": linkedCursors4, + "14": linkedCursors4, + "15": linkedCursors3, }); \ No newline at end of file