Преглед изворни кода

Only rebuild changed line indentation decorations (#16092)

GitOrigin-RevId: 13855598e2cfc06d2ea2357d1c71875993a106a9
Alf Eaton пре 2 година
родитељ
комит
c4b05f7a73

+ 67 - 6
services/web/frontend/js/features/source-editor/extensions/line-wrapping-indentation.ts

@@ -78,12 +78,14 @@ export const lineWrappingIndentation = (visual: boolean) => {
                 update(update: ViewUpdate) {
                   const maxIndent = view.state.field(field)
 
-                  if (
-                    maxIndent !== previousMaxIndent ||
-                    update.geometryChanged ||
-                    update.viewportChanged
-                  ) {
+                  if (maxIndent !== previousMaxIndent) {
                     value.decorations = buildDecorations(view, maxIndent)
+                  } else if (update.geometryChanged || update.viewportChanged) {
+                    value.decorations = updateDecorations(
+                      value.decorations,
+                      update,
+                      maxIndent
+                    )
                   }
 
                   previousMaxIndent = maxIndent
@@ -111,7 +113,6 @@ export const buildDecorations = (view: EditorView, maxIndent: number) => {
   let from = 0
 
   for (const line of doc.iterLines()) {
-    // const indent = line.match(/^(\s*)/)[1].length
     const indent = calculateIndent(line, tabSize, maxIndent)
 
     if (indent) {
@@ -124,6 +125,66 @@ export const buildDecorations = (view: EditorView, maxIndent: number) => {
   return Decoration.set(decorations)
 }
 
+export const updateDecorations = (
+  decorations: DecorationSet,
+  update: ViewUpdate,
+  maxIndent: number
+) => {
+  const add: Range<Decoration>[] = []
+
+  const { doc, tabSize } = update.state
+
+  const changedLinesFrom = new Set()
+
+  let filterFrom = doc.length
+  let filterTo = 0
+
+  update.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
+    // remove changed lines
+    const fromALineNumber = doc.lineAt(fromA).number
+    const toALineNumber = doc.lineAt(toA).number
+
+    for (
+      let lineNumber = fromALineNumber;
+      lineNumber <= toALineNumber;
+      lineNumber++
+    ) {
+      const line = doc.line(lineNumber)
+      changedLinesFrom.add(line.from)
+      filterFrom = Math.min(line.from, filterFrom)
+      filterTo = Math.max(line.from, filterTo)
+    }
+
+    // add changed lines
+    const fromBLineNumber = doc.lineAt(fromB).number
+    const toBLineNumber = doc.lineAt(toB).number
+
+    for (
+      let lineNumber = fromBLineNumber;
+      lineNumber <= toBLineNumber;
+      lineNumber++
+    ) {
+      const line = doc.line(lineNumber)
+      const indent = calculateIndent(line.text, tabSize, maxIndent)
+
+      if (indent) {
+        add.push(lineIndentDecoration(indent).range(line.from))
+      }
+    }
+  })
+
+  return decorations
+    .update({
+      filter(from) {
+        return !changedLinesFrom.has(from)
+      },
+      filterFrom,
+      filterTo,
+    })
+    .map(update.changes)
+    .update({ add })
+}
+
 const lineIndentDecoration = (indent: number) =>
   Decoration.line({
     attributes: {