399399 <DebugPanel />
400400
401401 <!-- AI 代码操作(解释/重构/生成测试) -->
402- <AiCodeAction v-if =" aiCodeCtx " :language =" currentLanguage " :code =" aiCodeCtx .code " :action =" aiCodeCtx .action "
402+ <AiCodeAction v-if =" aiCodeCtx " :language =" currentLanguage " :code =" aiCodeCtx .code " :action =" aiCodeCtx .action " : diagnostics = " aiCodeCtx . diagnostics "
403403 @replace =" onAiReplace " @insert =" onAiInsert " @close =" aiCodeCtx = null " />
404404
405405 <!-- .gitignore 模板 -->
463463 <button class =" w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer" @click =" aiCodeAction('explain')" >{{ t('aiCode.title.explain') }}</button >
464464 <button class =" w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer" @click =" aiCodeAction('refactor')" >{{ t('aiCode.title.refactor') }}</button >
465465 <button class =" w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer" @click =" aiCodeAction('test')" >{{ t('aiCode.title.test') }}</button >
466+ <button v-if =" canBlame || editorCtx.lsp" class =" w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer" @click =" aiFixDiagnostics" >{{ t('aiCode.title.fix') }}</button >
466467 <div class =" border-t border-gray-100 dark:border-gray-700 my-1" ></div >
467468 <button class =" w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer" @click =" sendToTerminal" >
468469 {{ t('app.sendToTerminal') }}
@@ -510,7 +511,7 @@ import {debounce} from 'lodash-es'
510511import {formatDocument , formatSelection , renameSymbol } from ' codemirror-languageserver'
511512import {runGotoDefinition , lspSupportsLanguage , triggerCodeActions , applyCodeAction , formatDocumentAsync } from ' ./editor/lspExtension'
512513import {dapSupportsLanguage } from ' ./debug/dapClient'
513- import {ArrowDownAZ , ArrowUpAZ , CaseLower , CaseUpper , ChevronRight , Code2 , CornerDownRight , Eraser , Eye , FolderOpen , GitBranch , GitCompare , History , ListChecks , ListTree , Maximize2 , Minimize2 , Monitor , Moon , PanelBottom , PanelLeft , PanelRight , Play , Plus , Save , Search , Settings as SettingsIcon , Sparkles , Sun , Terminal as TerminalIcon , WrapText , X } from ' lucide-vue-next'
514+ import {ArrowDownAZ , ArrowUpAZ , Bookmark , CaseLower , CaseUpper , ChevronRight , Code2 , CornerDownRight , Eraser , Eye , FoldVertical , FolderOpen , GitBranch , GitCompare , History , ListChecks , ListTree , Maximize2 , Minimize2 , Monitor , Moon , PanelBottom , PanelLeft , PanelRight , Play , Plus , Save , Search , Settings as SettingsIcon , Sparkles , Sun , Terminal as TerminalIcon , UnfoldVertical , WrapText , X } from ' lucide-vue-next'
514515import {ExecutionResult , LayoutMode , SplitDirection } from ' ./types/app.ts'
515516import AppHeader from ' ./components/AppHeader.vue'
516517import CodeEditor from ' ./components/CodeEditor.vue'
@@ -541,6 +542,9 @@ import {useFileManager} from './composables/useFileManager'
541542import {useLanguageRegistry } from ' ./composables/useLanguageRegistry'
542543import {useWorkspace } from ' ./composables/useWorkspace'
543544import {useTextCommands } from ' ./composables/useTextCommands'
545+ import {useBookmarks } from ' ./composables/useBookmarks'
546+ import {foldAll , unfoldAll , matchBrackets } from ' @codemirror/language'
547+ import {diagnostics } from ' ./editor/lspDiagnostics'
544548import {useGitPermalink } from ' ./composables/useGitPermalink'
545549import {useRevealInTree } from ' ./composables/useRevealInTree'
546550import {useWorkspaceRoots } from ' ./composables/useWorkspaceRoots'
@@ -1117,6 +1121,9 @@ const editorView = shallowRef<any>(null)
11171121// ===== 文本变换命令(排序行/大小写/去重/去行尾空白)=====
11181122const {transformSelectionOrLine, sortLines, removeDuplicateLines, trimTrailingWhitespace} = useTextCommands (editorView )
11191123
1124+ // ===== 行书签(切换/上一处/下一处/清空,按文件记忆)=====
1125+ const {toggleBookmark, nextBookmark, prevBookmark, clearBookmarks} = useBookmarks (editorView , currentFilePath )
1126+
11201127// 复制为 Markdown 代码块(选区或全文,带语言围栏)
11211128const copyAsMarkdown = async () => {
11221129 const view = editorView .value
@@ -1168,6 +1175,23 @@ const convertIndentation = (toTabs: boolean) => {
11681175 toast .success (t (' app.indentConverted' ))
11691176}
11701177
1178+ // 转到匹配括号:取光标前后的括号,跳到其配对处
1179+ const goToMatchingBracket = () => {
1180+ const view = editorView .value
1181+ if (! view ) {
1182+ return
1183+ }
1184+ const pos = view .state .selection .main .head
1185+ const m = matchBrackets (view .state , pos , - 1 ) || matchBrackets (view .state , pos , 1 )
1186+ if (m && m .matched && m .end ) {
1187+ view .dispatch ({selection: {anchor: m .end .from }, scrollIntoView: true })
1188+ view .focus ()
1189+ }
1190+ else {
1191+ toast .info (t (' app.noMatchingBracket' ))
1192+ }
1193+ }
1194+
11711195// AI 自然语言生成 / 选区改写
11721196const showGenerate = ref (false )
11731197const generateSelection = ref (' ' )
@@ -1398,7 +1422,21 @@ const runTests = async () => {
13981422}
13991423
14001424// C2:对选区(无选区则整篇)执行 AI 操作:解释 / 重构 / 生成测试
1401- const aiCodeCtx = ref <{action: ' explain' | ' refactor' | ' test' ; code: string ; from: number ; to: number } | null >(null )
1425+ const aiCodeCtx = ref <{action: ' explain' | ' refactor' | ' test' | ' fix' ; code: string ; from: number ; to: number ; diagnostics? : string } | null >(null )
1426+ // AI 修复诊断:把当前文件的 LSP 诊断交给 AI 修复整篇
1427+ const aiFixDiagnostics = () => {
1428+ closeEditorCtx ()
1429+ const view = editorView .value
1430+ if (! view ) {
1431+ return
1432+ }
1433+ if (! diagnostics .value .length ) {
1434+ toast .info (t (' app.noDiagnostics' ))
1435+ return
1436+ }
1437+ const diagText = diagnostics .value .map (d => ` [${d .severity }] L${d .line }:${d .col } ${d .message } ` ).join (' \n ' )
1438+ aiCodeCtx .value = {action: ' fix' , code: view .state .doc .toString (), from: 0 , to: view .state .doc .length , diagnostics: diagText }
1439+ }
14021440const aiCodeAction = (action : ' explain' | ' refactor' | ' test' ) => {
14031441 closeEditorCtx ()
14041442 const view = editorView .value
@@ -2154,7 +2192,8 @@ const shortcutDispatch: Record<string, () => void> = {
21542192 reopenClosed : () => handleReopenClosed (),
21552193 toggleSidebar : () => toggleSidebar (),
21562194 toggleTerminal : () => toggleTerminal (),
2157- toggleWordWrap : () => toggleWordWrap ()
2195+ toggleWordWrap : () => toggleWordWrap (),
2196+ toggleBookmark : () => toggleBookmark ()
21582197}
21592198
21602199// 切换自动换行(即时生效并随编辑器配置持久化)
@@ -2205,6 +2244,7 @@ const paletteCommands = computed<PaletteCommand[]>(() => [
22052244 {id: ' explainCode' , label: t (' command.explainCode' ), icon: Sparkles , run : () => explainCode ()},
22062245 {id: ' generateTests' , label: t (' command.generateTests' ), icon: Sparkles , run : () => generateTests ()},
22072246 {id: ' formatWithAi' , label: t (' command.formatWithAi' ), icon: Sparkles , run : () => formatWithAi ()},
2247+ {id: ' aiFixDiagnostics' , label: t (' command.aiFixDiagnostics' ), icon: Sparkles , run : () => aiFixDiagnostics ()},
22082248 {id: ' history' , label: t (' command.history' ), icon: History , run : () => { showHistory .value = true }},
22092249 {id: ' diff' , label: t (' command.diff' ), icon: GitCompare , run : () => openDiff ()},
22102250 {id: ' compareClipboard' , label: t (' command.compareClipboard' ), icon: GitCompare , run : () => compareWithClipboard ()},
@@ -2227,6 +2267,13 @@ const paletteCommands = computed<PaletteCommand[]>(() => [
22272267 {id: ' copyAsMarkdown' , label: t (' command.copyAsMarkdown' ), group: t (' command.groupText' ), icon: Code2 , run : () => copyAsMarkdown ()},
22282268 {id: ' indentToSpaces' , label: t (' command.indentToSpaces' ), group: t (' command.groupText' ), icon: Eraser , run : () => convertIndentation (false )},
22292269 {id: ' indentToTabs' , label: t (' command.indentToTabs' ), group: t (' command.groupText' ), icon: Eraser , run : () => convertIndentation (true )},
2270+ {id: ' foldAll' , label: t (' command.foldAll' ), group: t (' command.groupCode' ), icon: FoldVertical , run : () => { if (editorView .value ) foldAll (editorView .value ) }},
2271+ {id: ' unfoldAll' , label: t (' command.unfoldAll' ), group: t (' command.groupCode' ), icon: UnfoldVertical , run : () => { if (editorView .value ) unfoldAll (editorView .value ) }},
2272+ {id: ' goToMatchingBracket' , label: t (' command.goToMatchingBracket' ), group: t (' command.groupCode' ), icon: Code2 , run : () => goToMatchingBracket ()},
2273+ {id: ' toggleBookmark' , label: t (' command.toggleBookmark' ), group: t (' command.groupBookmark' ), icon: Bookmark , hint: hintOf (' toggleBookmark' ), run : () => toggleBookmark ()},
2274+ {id: ' nextBookmark' , label: t (' command.nextBookmark' ), group: t (' command.groupBookmark' ), icon: Bookmark , run : () => nextBookmark ()},
2275+ {id: ' prevBookmark' , label: t (' command.prevBookmark' ), group: t (' command.groupBookmark' ), icon: Bookmark , run : () => prevBookmark ()},
2276+ {id: ' clearBookmarks' , label: t (' command.clearBookmarks' ), group: t (' command.groupBookmark' ), icon: Bookmark , run : () => clearBookmarks ()},
22302277 {id: ' toggleAutoReveal' , label: t (' command.toggleAutoReveal' ), icon: FolderOpen , run : () => toggleAutoReveal ()},
22312278 {id: ' toggleSidebar' , label: t (' command.toggleSidebar' ), icon: PanelLeft , hint: hintOf (' toggleSidebar' ), run : () => toggleSidebar ()},
22322279 {id: ' toggleZen' , label: t (' command.toggleZen' ), icon: Minimize2 , run : () => toggleZen ()},
0 commit comments