Просмотр исходного кода

tui: restore full text when editing prompts with summarized content (#4030)

Ron Suhodrev 3 месяцев назад
Родитель
Сommit
dc7c5ced4c
1 измененных файлов с 73 добавлено и 8 удалено
  1. 73 8
      packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

+ 73 - 8
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

@@ -101,16 +101,81 @@ export function Prompt(props: PromptProps) {
         value: "prompt.editor",
         onSelect: async (dialog, trigger) => {
           dialog.clear()
-          const value = trigger === "prompt" ? "" : input.plainText
+
+          // replace summarized text parts with the actual text
+          const text = store.prompt.parts
+            .filter((p) => p.type === "text")
+            .reduce((acc, p) => {
+              if (!p.source) return acc
+              return acc.replace(p.source.text.value, p.text)
+            }, store.prompt.input)
+
+          const nonTextParts = store.prompt.parts.filter((p) => p.type !== "text")
+
+          const value = trigger === "prompt" ? "" : text
           const content = await Editor.open({ value, renderer })
-          if (content) {
-            input.setText(content, { history: false })
-            setStore("prompt", {
-              input: content,
-              parts: [],
+          if (!content) return
+
+          input.setText(content, { history: false })
+
+          // Update positions for nonTextParts based on their location in new content
+          // Filter out parts whose virtual text was deleted
+          // this handles a case where the user edits the text in the editor
+          // such that the virtual text moves around or is deleted
+          const updatedNonTextParts = nonTextParts
+            .map((part) => {
+              let virtualText = ""
+              if (part.type === "file" && part.source?.text) {
+                virtualText = part.source.text.value
+              } else if (part.type === "agent" && part.source) {
+                virtualText = part.source.value
+              }
+
+              if (!virtualText) return part
+
+              const newStart = content.indexOf(virtualText)
+              // if the virtual text is deleted, remove the part
+              if (newStart === -1) return null
+
+              const newEnd = newStart + virtualText.length
+
+              if (part.type === "file" && part.source?.text) {
+                return {
+                  ...part,
+                  source: {
+                    ...part.source,
+                    text: {
+                      ...part.source.text,
+                      start: newStart,
+                      end: newEnd,
+                    },
+                  },
+                }
+              }
+
+              if (part.type === "agent" && part.source) {
+                return {
+                  ...part,
+                  source: {
+                    ...part.source,
+                    start: newStart,
+                    end: newEnd,
+                  },
+                }
+              }
+
+              return part
             })
-            input.cursorOffset = Bun.stringWidth(content)
-          }
+            .filter((part) => part !== null)
+
+          setStore("prompt", {
+            input: content,
+            // keep only the non-text parts because the text parts were
+            // already expanded inline
+            parts: updatedNonTextParts,
+          })
+          restoreExtmarksFromParts(updatedNonTextParts)
+          input.cursorOffset = Bun.stringWidth(content)
         },
       },
       {