@@ -492,7 +492,7 @@ import {debounce} from 'lodash-es'
492492import {formatDocument , formatSelection , renameSymbol } from ' codemirror-languageserver'
493493import {runGotoDefinition , lspSupportsLanguage , triggerCodeActions , applyCodeAction , formatDocumentAsync } from ' ./editor/lspExtension'
494494import {dapSupportsLanguage } from ' ./debug/dapClient'
495- import {ArrowDownAZ , ArrowUpAZ , CaseLower , CaseUpper , ChevronRight , Code2 , CornerDownRight , Eye , FolderOpen , GitBranch , GitCompare , History , ListChecks , ListTree , Maximize2 , Monitor , Moon , PanelBottom , PanelLeft , PanelRight , Play , Plus , Save , Search , Settings as SettingsIcon , Sparkles , Sun , Terminal as TerminalIcon , WrapText , X } from ' lucide-vue-next'
495+ import {ArrowDownAZ , ArrowUpAZ , CaseLower , CaseUpper , ChevronRight , Code2 , CornerDownRight , Eraser , Eye , FolderOpen , GitBranch , GitCompare , History , ListChecks , ListTree , Maximize2 , Monitor , Moon , PanelBottom , PanelLeft , PanelRight , Play , Plus , Save , Search , Settings as SettingsIcon , Sparkles , Sun , Terminal as TerminalIcon , WrapText , X } from ' lucide-vue-next'
496496import {ExecutionResult , LayoutMode , SplitDirection } from ' ./types/app.ts'
497497import AppHeader from ' ./components/AppHeader.vue'
498498import CodeEditor from ' ./components/CodeEditor.vue'
@@ -776,6 +776,44 @@ const sortLines = (desc: boolean) => {
776776 view .dispatch ({changes: {from , to , insert: out }, selection: {anchor: from , head: from + out .length }})
777777 view .focus ()
778778}
779+ // 选中行(无选区则全文)按整行的范围
780+ const lineBlockRange = () => {
781+ const view = editorView .value !
782+ const sel = view .state .selection .main
783+ const from = sel .empty ? 0 : view .state .doc .lineAt (sel .from ).from
784+ const to = sel .empty ? view .state .doc .length : view .state .doc .lineAt (sel .to ).to
785+ return {from , to }
786+ }
787+ // 删除重复行(保留首次出现)
788+ const removeDuplicateLines = () => {
789+ const view = editorView .value
790+ if (! view ) {
791+ return
792+ }
793+ const {from, to} = lineBlockRange ()
794+ const seen = new Set <string >()
795+ const out: string [] = []
796+ for (const l of view .state .doc .sliceString (from , to ).split (' \n ' )) {
797+ if (! seen .has (l )) {
798+ seen .add (l )
799+ out .push (l )
800+ }
801+ }
802+ const text = out .join (' \n ' )
803+ view .dispatch ({changes: {from , to , insert: text }, selection: {anchor: from , head: from + text .length }})
804+ view .focus ()
805+ }
806+ // 去除行尾空白
807+ const trimTrailingWhitespace = () => {
808+ const view = editorView .value
809+ if (! view ) {
810+ return
811+ }
812+ const {from, to} = lineBlockRange ()
813+ const text = view .state .doc .sliceString (from , to ).replace (/ [ \t ] + (\r ? \n )/ g , ' $1' ).replace (/ [ \t ] + $ / , ' ' )
814+ view .dispatch ({changes: {from , to , insert: text }, selection: {anchor: from , head: from + text .length }})
815+ view .focus ()
816+ }
779817
780818const handleCopyRelativePath = (path : string ) => {
781819 const root = rootDir .value
@@ -2426,6 +2464,8 @@ const paletteCommands = computed<PaletteCommand[]>(() => [
24262464 {id: ' sortLinesDesc' , label: t (' command.sortLinesDesc' ), group: t (' command.groupText' ), icon: ArrowUpAZ , run : () => sortLines (true )},
24272465 {id: ' toUpperCase' , label: t (' command.toUpperCase' ), group: t (' command.groupText' ), icon: CaseUpper , run : () => transformSelectionOrLine (s => s .toUpperCase ())},
24282466 {id: ' toLowerCase' , label: t (' command.toLowerCase' ), group: t (' command.groupText' ), icon: CaseLower , run : () => transformSelectionOrLine (s => s .toLowerCase ())},
2467+ {id: ' removeDuplicateLines' , label: t (' command.removeDuplicateLines' ), group: t (' command.groupText' ), icon: ListChecks , run : () => removeDuplicateLines ()},
2468+ {id: ' trimTrailingWhitespace' , label: t (' command.trimTrailingWhitespace' ), group: t (' command.groupText' ), icon: Eraser , run : () => trimTrailingWhitespace ()},
24292469 {id: ' toggleAutoReveal' , label: t (' command.toggleAutoReveal' ), icon: FolderOpen , run : () => toggleAutoReveal ()},
24302470 {id: ' toggleSidebar' , label: t (' command.toggleSidebar' ), icon: PanelLeft , hint: hintOf (' toggleSidebar' ), run : () => toggleSidebar ()},
24312471 {id: ' toggleWordWrap' , label: t (' command.toggleWordWrap' ), icon: WrapText , hint: hintOf (' toggleWordWrap' ), run : () => toggleWordWrap ()},
0 commit comments