From 5a1a41aba1d5b74407ec7f87eff8b410bfbaafaa Mon Sep 17 00:00:00 2001
From: Vitaly <vital2580@icloud.com>
Date: Tue, 28 Nov 2023 03:51:26 +0300
Subject: [PATCH] feat: Speed up a bit JSX linked editing by simply caching
 previous response

---
 src/configurationType.ts                |  6 ++++
 typescript/src/decorateLinkedEditing.ts | 37 +++++++++++++++++++++++++
 typescript/src/decorateProxy.ts         |  2 ++
 3 files changed, 45 insertions(+)
 create mode 100644 typescript/src/decorateLinkedEditing.ts

diff --git a/src/configurationType.ts b/src/configurationType.ts
index b2ac606..73981b7 100644
--- a/src/configurationType.ts
+++ b/src/configurationType.ts
@@ -582,6 +582,12 @@ export type Configuration = {
      * @default false
      */
     'experiments.enableInsertNameOfSuggestionFix': boolean
+    /**
+     * Speed up JSX linked editing by not using actual tsserver command when possible, which in theory may introduce some inconsistencies.
+     * Note that currently it doesn't really help if you have `"typescript.tsserver.useSyntaxServer": "auto"` in the settings.
+     * @default true
+     */
+    'experiments.speedLinkedEditing': boolean
     /**
      * Map *symbol - array of modules* to change sorting of imports - first available takes precedence in auto import code fixes (+ import all action)
      *
diff --git a/typescript/src/decorateLinkedEditing.ts b/typescript/src/decorateLinkedEditing.ts
new file mode 100644
index 0000000..a21705d
--- /dev/null
+++ b/typescript/src/decorateLinkedEditing.ts
@@ -0,0 +1,37 @@
+import { GetConfig } from './types'
+
+export default (proxy: ts.LanguageService, languageService: ts.LanguageService, languageServiceHost: ts.LanguageServiceHost, c: GetConfig) => {
+    // patch JSX tag linked editing to improve performance (needed for great user experience)
+
+    let lastLinkedEditingRangeRequest:
+        | {
+              pos: number
+              fileName: string
+              result: ts.LinkedEditingInfo
+          }
+        | undefined
+    proxy.getLinkedEditingRangeAtPosition = (fileName, position) => {
+        if (
+            c('experiments.speedLinkedEditing') &&
+            lastLinkedEditingRangeRequest &&
+            lastLinkedEditingRangeRequest.pos === position - 1 &&
+            lastLinkedEditingRangeRequest.fileName === fileName
+        ) {
+            lastLinkedEditingRangeRequest.pos = position
+            lastLinkedEditingRangeRequest.result.ranges[0]!.length++
+            lastLinkedEditingRangeRequest.result.ranges[1]!.start++
+            lastLinkedEditingRangeRequest.result.ranges[1]!.length++
+            return lastLinkedEditingRangeRequest.result
+        }
+        lastLinkedEditingRangeRequest = undefined
+
+        const prior = languageService.getLinkedEditingRangeAtPosition(fileName, position)
+        if (!prior) return
+        lastLinkedEditingRangeRequest = {
+            pos: position,
+            fileName,
+            result: globalThis.structuredClone(prior),
+        }
+        return prior
+    }
+}
diff --git a/typescript/src/decorateProxy.ts b/typescript/src/decorateProxy.ts
index 387c85c..6886ae7 100644
--- a/typescript/src/decorateProxy.ts
+++ b/typescript/src/decorateProxy.ts
@@ -19,6 +19,7 @@ import decorateSignatureHelp from './decorateSignatureHelp'
 import decorateFindRenameLocations from './decorateFindRenameLocations'
 import decorateQuickInfoAtPosition from './decorateQuickInfoAtPosition'
 import decorateEditsForFileRename from './decorateEditsForFileRename'
+import decorateLinkedEditing from './decorateLinkedEditing'
 
 /** @internal */
 export const thisPluginMarker = '__essentialPluginsMarker__'
@@ -102,6 +103,7 @@ export const decorateLanguageService = (
     decorateSignatureHelp(proxy, languageService, languageServiceHost, c)
     decorateFindRenameLocations(proxy, languageService, c)
     decorateQuickInfoAtPosition(proxy, languageService, languageServiceHost, c)
+    decorateLinkedEditing(proxy, languageService, languageServiceHost, c)
 
     libDomPatching(languageServiceHost, c)