Skip to content

Commit 17a0ad3

Browse files
committed
feat(editor): 文本命令补充——删除重复行、去除行尾空白
「文本」分组新增两项:删除重复行(保留首次出现)、去除行尾空白; 作用于选区,无选区时作用于全文,复用按行范围逻辑。
1 parent 5a519bb commit 17a0ad3

3 files changed

Lines changed: 45 additions & 1 deletion

File tree

src/App.vue

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ import {debounce} from 'lodash-es'
492492
import {formatDocument, formatSelection, renameSymbol} from 'codemirror-languageserver'
493493
import {runGotoDefinition, lspSupportsLanguage, triggerCodeActions, applyCodeAction, formatDocumentAsync} from './editor/lspExtension'
494494
import {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'
496496
import {ExecutionResult, LayoutMode, SplitDirection} from './types/app.ts'
497497
import AppHeader from './components/AppHeader.vue'
498498
import 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
780818
const 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()},

src/i18n/locales/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,8 @@
700700
"sortLinesDesc": "Sort lines descending",
701701
"toUpperCase": "Transform to uppercase",
702702
"toLowerCase": "Transform to lowercase",
703+
"removeDuplicateLines": "Remove duplicate lines",
704+
"trimTrailingWhitespace": "Trim trailing whitespace",
703705
"groupText": "Text",
704706
"toggleAutoReveal": "Toggle: auto-reveal active file",
705707
"toggleSidebar": "Toggle sidebar",

src/i18n/locales/zh-CN.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,8 @@
700700
"sortLinesDesc": "排序行(降序)",
701701
"toUpperCase": "转为大写",
702702
"toLowerCase": "转为小写",
703+
"removeDuplicateLines": "删除重复行",
704+
"trimTrailingWhitespace": "去除行尾空白",
703705
"groupText": "文本",
704706
"toggleAutoReveal": "切换:自动定位当前文件",
705707
"toggleSidebar": "切换侧栏",

0 commit comments

Comments
 (0)