✨ Unified management of keymap
This commit is contained in:
@@ -15,15 +15,13 @@ import {
|
||||
bracketMatching,
|
||||
defaultHighlightStyle,
|
||||
foldGutter,
|
||||
foldKeymap,
|
||||
indentOnInput,
|
||||
syntaxHighlighting,
|
||||
} from '@codemirror/language';
|
||||
import {defaultKeymap, history, historyKeymap,} from '@codemirror/commands';
|
||||
import {history} from '@codemirror/commands';
|
||||
import {highlightSelectionMatches} from '@codemirror/search';
|
||||
import {autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap} from '@codemirror/autocomplete';
|
||||
import {lintKeymap} from '@codemirror/lint';
|
||||
import {customSearchKeymap, searchVisibilityField, vscodeSearch} from './vscodeSearch';
|
||||
import {autocompletion, closeBrackets, closeBracketsKeymap} from '@codemirror/autocomplete';
|
||||
import {searchVisibilityField, vscodeSearch} from './vscodeSearch';
|
||||
|
||||
import {hyperLink} from './hyperlink';
|
||||
import {color} from './colorSelector';
|
||||
@@ -91,13 +89,7 @@ export const createBasicSetup = (): Extension[] => {
|
||||
|
||||
// 键盘映射
|
||||
keymap.of([
|
||||
...customSearchKeymap,
|
||||
...closeBracketsKeymap,
|
||||
...defaultKeymap,
|
||||
...historyKeymap,
|
||||
...foldKeymap,
|
||||
...completionKeymap,
|
||||
...lintKeymap
|
||||
]),
|
||||
];
|
||||
};
|
||||
@@ -208,27 +208,4 @@ export function getCopyPasteExtensions() {
|
||||
return [
|
||||
codeBlockCopyCut,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取复制粘贴键盘映射
|
||||
*/
|
||||
export function getCopyPasteKeymap() {
|
||||
return [
|
||||
{
|
||||
key: 'Mod-c',
|
||||
run: copyCommand,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Mod-x',
|
||||
run: cutCommand,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Mod-v',
|
||||
run: pasteCommand,
|
||||
preventDefault: true
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,14 @@
|
||||
*/
|
||||
|
||||
import {Extension} from '@codemirror/state';
|
||||
import {keymap, lineNumbers} from '@codemirror/view';
|
||||
import {lineNumbers} from '@codemirror/view';
|
||||
|
||||
// 导入核心模块
|
||||
import {blockState} from './state';
|
||||
import {getBlockDecorationExtensions} from './decorations';
|
||||
import * as commands from './commands';
|
||||
import {getBlockSelectExtensions, selectAll} from './selectAll';
|
||||
import {getCopyPasteExtensions, getCopyPasteKeymap} from './copyPaste';
|
||||
import {deleteLineCommand} from './deleteLine';
|
||||
import {getBlockSelectExtensions} from './selectAll';
|
||||
import {getCopyPasteExtensions} from './copyPaste';
|
||||
import {moveLineDown, moveLineUp} from './moveLines';
|
||||
import {transposeChars} from './transposeChars';
|
||||
import {getCodeBlockLanguageExtension} from './lang-parser';
|
||||
import {createLanguageDetection} from './language-detection';
|
||||
import {EditorOptions, SupportedLanguage} from './types';
|
||||
@@ -92,13 +89,7 @@ export function createCodeBlockExtension(options: CodeBlockOptions = {}): Extens
|
||||
defaultAutoDetect = true,
|
||||
} = options;
|
||||
|
||||
// 将简化的配置转换为内部使用的EditorOptions
|
||||
const editorOptions: EditorOptions = {
|
||||
defaultBlockToken: defaultLanguage,
|
||||
defaultBlockAutoDetect: defaultAutoDetect,
|
||||
};
|
||||
|
||||
const extensions: Extension[] = [
|
||||
return [
|
||||
// 核心状态管理
|
||||
blockState,
|
||||
|
||||
@@ -126,105 +117,7 @@ export function createCodeBlockExtension(options: CodeBlockOptions = {}): Extens
|
||||
// 复制粘贴功能
|
||||
...getCopyPasteExtensions(),
|
||||
|
||||
// 键盘映射
|
||||
keymap.of([
|
||||
// 复制粘贴命令
|
||||
...getCopyPasteKeymap(),
|
||||
|
||||
// 块隔离选择命令
|
||||
{
|
||||
key: 'Mod-a',
|
||||
run: selectAll,
|
||||
preventDefault: true
|
||||
},
|
||||
// 块创建命令
|
||||
{
|
||||
key: 'Mod-Enter',
|
||||
run: commands.addNewBlockAfterCurrent(editorOptions),
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Mod-Shift-Enter',
|
||||
run: commands.addNewBlockAfterLast(editorOptions),
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Alt-Enter',
|
||||
run: commands.addNewBlockBeforeCurrent(editorOptions),
|
||||
preventDefault: true
|
||||
},
|
||||
|
||||
// 块导航命令
|
||||
{
|
||||
key: 'Mod-ArrowUp',
|
||||
run: commands.gotoPreviousBlock,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Mod-ArrowDown',
|
||||
run: commands.gotoNextBlock,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Mod-Shift-ArrowUp',
|
||||
run: commands.selectPreviousBlock,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Mod-Shift-ArrowDown',
|
||||
run: commands.selectNextBlock,
|
||||
preventDefault: true
|
||||
},
|
||||
|
||||
// 块编辑命令
|
||||
{
|
||||
key: 'Mod-Shift-d',
|
||||
run: commands.deleteBlock(editorOptions),
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Alt-Mod-ArrowUp',
|
||||
run: commands.moveCurrentBlockUp,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Alt-Mod-ArrowDown',
|
||||
run: commands.moveCurrentBlockDown,
|
||||
preventDefault: true
|
||||
},
|
||||
|
||||
// 行编辑命令
|
||||
{
|
||||
key: 'Mod-Shift-k', // 删除行
|
||||
run: deleteLineCommand,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Alt-ArrowUp', // 向上移动行
|
||||
run: moveLineUp,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Alt-ArrowDown', // 向下移动行
|
||||
run: moveLineDown,
|
||||
preventDefault: true
|
||||
},
|
||||
{
|
||||
key: 'Ctrl-t', // 字符转置
|
||||
run: transposeChars,
|
||||
preventDefault: true
|
||||
},
|
||||
|
||||
// 代码格式化命令
|
||||
{
|
||||
key: 'Mod-Shift-f', // 格式化代码
|
||||
run: commands.formatCurrentBlock,
|
||||
preventDefault: true
|
||||
},
|
||||
])
|
||||
];
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
|
||||
@@ -271,7 +164,6 @@ export {
|
||||
cutCommand,
|
||||
pasteCommand,
|
||||
getCopyPasteExtensions,
|
||||
getCopyPasteKeymap
|
||||
} from './copyPaste';
|
||||
|
||||
// 删除行功能
|
||||
|
||||
@@ -218,7 +218,5 @@ function isInDelimiter(state: any, pos: number) {
|
||||
export function getBlockSelectExtensions() {
|
||||
return [
|
||||
emptyBlockSelected,
|
||||
// 禁用块边界检查以避免递归更新问题
|
||||
// blockAwareSelection,
|
||||
];
|
||||
}
|
||||
@@ -6,4 +6,5 @@ export * from './autoSaveExtension';
|
||||
export * from './fontExtension';
|
||||
export * from './themeExtension';
|
||||
export * from './codeblast';
|
||||
export * from './codeblock';
|
||||
export * from './codeblock';
|
||||
export * from './keymap';
|
||||
298
frontend/src/views/editor/extensions/keymap/commandRegistry.ts
Normal file
298
frontend/src/views/editor/extensions/keymap/commandRegistry.ts
Normal file
@@ -0,0 +1,298 @@
|
||||
import {KeyBindingCommand} from '@/../bindings/voidraft/internal/models/models'
|
||||
import {
|
||||
hideSearchVisibilityCommand,
|
||||
searchReplaceAll,
|
||||
searchShowReplace,
|
||||
searchToggleCase,
|
||||
searchToggleRegex,
|
||||
searchToggleWholeWord,
|
||||
showSearchVisibilityCommand
|
||||
} from '../vscodeSearch/commands'
|
||||
import {
|
||||
addNewBlockAfterCurrent,
|
||||
addNewBlockAfterLast,
|
||||
addNewBlockBeforeCurrent,
|
||||
deleteBlock,
|
||||
formatCurrentBlock,
|
||||
gotoNextBlock,
|
||||
gotoPreviousBlock,
|
||||
moveCurrentBlockDown,
|
||||
moveCurrentBlockUp,
|
||||
selectNextBlock,
|
||||
selectPreviousBlock
|
||||
} from '../codeblock/commands'
|
||||
import { selectAll } from '../codeblock/selectAll'
|
||||
import { deleteLineCommand } from '../codeblock/deleteLine'
|
||||
import { moveLineUp, moveLineDown } from '../codeblock/moveLines'
|
||||
import { transposeChars } from '@/views/editor/extensions'
|
||||
import { copyCommand, cutCommand, pasteCommand } from '../codeblock/copyPaste'
|
||||
import { undo, redo, undoSelection, redoSelection, cursorSyntaxLeft, cursorSyntaxRight, selectSyntaxLeft, selectSyntaxRight, copyLineUp, copyLineDown, insertBlankLine, selectLine, selectParentSyntax, indentLess, indentMore, indentSelection, cursorMatchingBracket, toggleComment, toggleBlockComment, insertNewlineAndIndent, deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward } from '@codemirror/commands'
|
||||
import { foldCode, unfoldCode, foldAll, unfoldAll } from '@codemirror/language'
|
||||
import i18n from '@/i18n'
|
||||
|
||||
// 默认编辑器选项
|
||||
const defaultEditorOptions = {
|
||||
defaultBlockToken: 'text',
|
||||
defaultBlockAutoDetect: true,
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端命令注册表
|
||||
* 将后端定义的command字段映射到具体的前端方法和翻译键
|
||||
*/
|
||||
export const commandRegistry = {
|
||||
[KeyBindingCommand.ShowSearchCommand]: {
|
||||
handler: showSearchVisibilityCommand,
|
||||
descriptionKey: 'keybindings.commands.showSearch'
|
||||
},
|
||||
[KeyBindingCommand.HideSearchCommand]: {
|
||||
handler: hideSearchVisibilityCommand,
|
||||
descriptionKey: 'keybindings.commands.hideSearch'
|
||||
},
|
||||
[KeyBindingCommand.SearchToggleCaseCommand]: {
|
||||
handler: searchToggleCase,
|
||||
descriptionKey: 'keybindings.commands.searchToggleCase'
|
||||
},
|
||||
[KeyBindingCommand.SearchToggleWordCommand]: {
|
||||
handler: searchToggleWholeWord,
|
||||
descriptionKey: 'keybindings.commands.searchToggleWord'
|
||||
},
|
||||
[KeyBindingCommand.SearchToggleRegexCommand]: {
|
||||
handler: searchToggleRegex,
|
||||
descriptionKey: 'keybindings.commands.searchToggleRegex'
|
||||
},
|
||||
[KeyBindingCommand.SearchShowReplaceCommand]: {
|
||||
handler: searchShowReplace,
|
||||
descriptionKey: 'keybindings.commands.searchShowReplace'
|
||||
},
|
||||
[KeyBindingCommand.SearchReplaceAllCommand]: {
|
||||
handler: searchReplaceAll,
|
||||
descriptionKey: 'keybindings.commands.searchReplaceAll'
|
||||
},
|
||||
|
||||
// 代码块操作命令
|
||||
[KeyBindingCommand.BlockSelectAllCommand]: {
|
||||
handler: selectAll,
|
||||
descriptionKey: 'keybindings.commands.blockSelectAll'
|
||||
},
|
||||
[KeyBindingCommand.BlockAddAfterCurrentCommand]: {
|
||||
handler: addNewBlockAfterCurrent(defaultEditorOptions),
|
||||
descriptionKey: 'keybindings.commands.blockAddAfterCurrent'
|
||||
},
|
||||
[KeyBindingCommand.BlockAddAfterLastCommand]: {
|
||||
handler: addNewBlockAfterLast(defaultEditorOptions),
|
||||
descriptionKey: 'keybindings.commands.blockAddAfterLast'
|
||||
},
|
||||
[KeyBindingCommand.BlockAddBeforeCurrentCommand]: {
|
||||
handler: addNewBlockBeforeCurrent(defaultEditorOptions),
|
||||
descriptionKey: 'keybindings.commands.blockAddBeforeCurrent'
|
||||
},
|
||||
[KeyBindingCommand.BlockGotoPreviousCommand]: {
|
||||
handler: gotoPreviousBlock,
|
||||
descriptionKey: 'keybindings.commands.blockGotoPrevious'
|
||||
},
|
||||
[KeyBindingCommand.BlockGotoNextCommand]: {
|
||||
handler: gotoNextBlock,
|
||||
descriptionKey: 'keybindings.commands.blockGotoNext'
|
||||
},
|
||||
[KeyBindingCommand.BlockSelectPreviousCommand]: {
|
||||
handler: selectPreviousBlock,
|
||||
descriptionKey: 'keybindings.commands.blockSelectPrevious'
|
||||
},
|
||||
[KeyBindingCommand.BlockSelectNextCommand]: {
|
||||
handler: selectNextBlock,
|
||||
descriptionKey: 'keybindings.commands.blockSelectNext'
|
||||
},
|
||||
[KeyBindingCommand.BlockDeleteCommand]: {
|
||||
handler: deleteBlock(defaultEditorOptions),
|
||||
descriptionKey: 'keybindings.commands.blockDelete'
|
||||
},
|
||||
[KeyBindingCommand.BlockMoveUpCommand]: {
|
||||
handler: moveCurrentBlockUp,
|
||||
descriptionKey: 'keybindings.commands.blockMoveUp'
|
||||
},
|
||||
[KeyBindingCommand.BlockMoveDownCommand]: {
|
||||
handler: moveCurrentBlockDown,
|
||||
descriptionKey: 'keybindings.commands.blockMoveDown'
|
||||
},
|
||||
[KeyBindingCommand.BlockDeleteLineCommand]: {
|
||||
handler: deleteLineCommand,
|
||||
descriptionKey: 'keybindings.commands.blockDeleteLine'
|
||||
},
|
||||
[KeyBindingCommand.BlockMoveLineUpCommand]: {
|
||||
handler: moveLineUp,
|
||||
descriptionKey: 'keybindings.commands.blockMoveLineUp'
|
||||
},
|
||||
[KeyBindingCommand.BlockMoveLineDownCommand]: {
|
||||
handler: moveLineDown,
|
||||
descriptionKey: 'keybindings.commands.blockMoveLineDown'
|
||||
},
|
||||
[KeyBindingCommand.BlockTransposeCharsCommand]: {
|
||||
handler: transposeChars,
|
||||
descriptionKey: 'keybindings.commands.blockTransposeChars'
|
||||
},
|
||||
[KeyBindingCommand.BlockFormatCommand]: {
|
||||
handler: formatCurrentBlock,
|
||||
descriptionKey: 'keybindings.commands.blockFormat'
|
||||
},
|
||||
[KeyBindingCommand.BlockCopyCommand]: {
|
||||
handler: copyCommand,
|
||||
descriptionKey: 'keybindings.commands.blockCopy'
|
||||
},
|
||||
[KeyBindingCommand.BlockCutCommand]: {
|
||||
handler: cutCommand,
|
||||
descriptionKey: 'keybindings.commands.blockCut'
|
||||
},
|
||||
[KeyBindingCommand.BlockPasteCommand]: {
|
||||
handler: pasteCommand,
|
||||
descriptionKey: 'keybindings.commands.blockPaste'
|
||||
},
|
||||
[KeyBindingCommand.HistoryUndoCommand]: {
|
||||
handler: undo,
|
||||
descriptionKey: 'keybindings.commands.historyUndo'
|
||||
},
|
||||
[KeyBindingCommand.HistoryRedoCommand]: {
|
||||
handler: redo,
|
||||
descriptionKey: 'keybindings.commands.historyRedo'
|
||||
},
|
||||
[KeyBindingCommand.HistoryUndoSelectionCommand]: {
|
||||
handler: undoSelection,
|
||||
descriptionKey: 'keybindings.commands.historyUndoSelection'
|
||||
},
|
||||
[KeyBindingCommand.HistoryRedoSelectionCommand]: {
|
||||
handler: redoSelection,
|
||||
descriptionKey: 'keybindings.commands.historyRedoSelection'
|
||||
},
|
||||
[KeyBindingCommand.FoldCodeCommand]: {
|
||||
handler: foldCode,
|
||||
descriptionKey: 'keybindings.commands.foldCode'
|
||||
},
|
||||
[KeyBindingCommand.UnfoldCodeCommand]: {
|
||||
handler: unfoldCode,
|
||||
descriptionKey: 'keybindings.commands.unfoldCode'
|
||||
},
|
||||
[KeyBindingCommand.FoldAllCommand]: {
|
||||
handler: foldAll,
|
||||
descriptionKey: 'keybindings.commands.foldAll'
|
||||
},
|
||||
[KeyBindingCommand.UnfoldAllCommand]: {
|
||||
handler: unfoldAll,
|
||||
descriptionKey: 'keybindings.commands.unfoldAll'
|
||||
},
|
||||
[KeyBindingCommand.CursorSyntaxLeftCommand]: {
|
||||
handler: cursorSyntaxLeft,
|
||||
descriptionKey: 'keybindings.commands.cursorSyntaxLeft'
|
||||
},
|
||||
[KeyBindingCommand.CursorSyntaxRightCommand]: {
|
||||
handler: cursorSyntaxRight,
|
||||
descriptionKey: 'keybindings.commands.cursorSyntaxRight'
|
||||
},
|
||||
[KeyBindingCommand.SelectSyntaxLeftCommand]: {
|
||||
handler: selectSyntaxLeft,
|
||||
descriptionKey: 'keybindings.commands.selectSyntaxLeft'
|
||||
},
|
||||
[KeyBindingCommand.SelectSyntaxRightCommand]: {
|
||||
handler: selectSyntaxRight,
|
||||
descriptionKey: 'keybindings.commands.selectSyntaxRight'
|
||||
},
|
||||
[KeyBindingCommand.CopyLineUpCommand]: {
|
||||
handler: copyLineUp,
|
||||
descriptionKey: 'keybindings.commands.copyLineUp'
|
||||
},
|
||||
[KeyBindingCommand.CopyLineDownCommand]: {
|
||||
handler: copyLineDown,
|
||||
descriptionKey: 'keybindings.commands.copyLineDown'
|
||||
},
|
||||
[KeyBindingCommand.InsertBlankLineCommand]: {
|
||||
handler: insertBlankLine,
|
||||
descriptionKey: 'keybindings.commands.insertBlankLine'
|
||||
},
|
||||
[KeyBindingCommand.SelectLineCommand]: {
|
||||
handler: selectLine,
|
||||
descriptionKey: 'keybindings.commands.selectLine'
|
||||
},
|
||||
[KeyBindingCommand.SelectParentSyntaxCommand]: {
|
||||
handler: selectParentSyntax,
|
||||
descriptionKey: 'keybindings.commands.selectParentSyntax'
|
||||
},
|
||||
[KeyBindingCommand.IndentLessCommand]: {
|
||||
handler: indentLess,
|
||||
descriptionKey: 'keybindings.commands.indentLess'
|
||||
},
|
||||
[KeyBindingCommand.IndentMoreCommand]: {
|
||||
handler: indentMore,
|
||||
descriptionKey: 'keybindings.commands.indentMore'
|
||||
},
|
||||
[KeyBindingCommand.IndentSelectionCommand]: {
|
||||
handler: indentSelection,
|
||||
descriptionKey: 'keybindings.commands.indentSelection'
|
||||
},
|
||||
[KeyBindingCommand.CursorMatchingBracketCommand]: {
|
||||
handler: cursorMatchingBracket,
|
||||
descriptionKey: 'keybindings.commands.cursorMatchingBracket'
|
||||
},
|
||||
[KeyBindingCommand.ToggleCommentCommand]: {
|
||||
handler: toggleComment,
|
||||
descriptionKey: 'keybindings.commands.toggleComment'
|
||||
},
|
||||
[KeyBindingCommand.ToggleBlockCommentCommand]: {
|
||||
handler: toggleBlockComment,
|
||||
descriptionKey: 'keybindings.commands.toggleBlockComment'
|
||||
},
|
||||
[KeyBindingCommand.InsertNewlineAndIndentCommand]: {
|
||||
handler: insertNewlineAndIndent,
|
||||
descriptionKey: 'keybindings.commands.insertNewlineAndIndent'
|
||||
},
|
||||
[KeyBindingCommand.DeleteCharBackwardCommand]: {
|
||||
handler: deleteCharBackward,
|
||||
descriptionKey: 'keybindings.commands.deleteCharBackward'
|
||||
},
|
||||
[KeyBindingCommand.DeleteCharForwardCommand]: {
|
||||
handler: deleteCharForward,
|
||||
descriptionKey: 'keybindings.commands.deleteCharForward'
|
||||
},
|
||||
[KeyBindingCommand.DeleteGroupBackwardCommand]: {
|
||||
handler: deleteGroupBackward,
|
||||
descriptionKey: 'keybindings.commands.deleteGroupBackward'
|
||||
},
|
||||
[KeyBindingCommand.DeleteGroupForwardCommand]: {
|
||||
handler: deleteGroupForward,
|
||||
descriptionKey: 'keybindings.commands.deleteGroupForward'
|
||||
},
|
||||
} as const
|
||||
|
||||
/**
|
||||
* 获取命令处理函数
|
||||
* @param command 命令名称
|
||||
* @returns 对应的处理函数,如果不存在则返回 undefined
|
||||
*/
|
||||
export const getCommandHandler = (command: KeyBindingCommand) => {
|
||||
return commandRegistry[command]?.handler
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取命令描述
|
||||
* @param command 命令名称
|
||||
* @returns 对应的描述,如果不存在则返回 undefined
|
||||
*/
|
||||
export const getCommandDescription = (command: KeyBindingCommand) => {
|
||||
const descriptionKey = commandRegistry[command]?.descriptionKey
|
||||
return descriptionKey ? i18n.global.t(descriptionKey) : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查命令是否已注册
|
||||
* @param command 命令名称
|
||||
* @returns 是否已注册
|
||||
*/
|
||||
export const isCommandRegistered = (command: KeyBindingCommand): boolean => {
|
||||
return command in commandRegistry
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已注册的命令
|
||||
* @returns 已注册的命令列表
|
||||
*/
|
||||
export const getRegisteredCommands = (): KeyBindingCommand[] => {
|
||||
return Object.keys(commandRegistry) as KeyBindingCommand[]
|
||||
}
|
||||
23
frontend/src/views/editor/extensions/keymap/index.ts
Normal file
23
frontend/src/views/editor/extensions/keymap/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Extension } from '@codemirror/state'
|
||||
import { useKeybindingStore } from '@/stores/keybindingStore'
|
||||
import { KeymapManager } from './keymapManager'
|
||||
|
||||
/**
|
||||
* 异步创建快捷键扩展
|
||||
* 确保快捷键配置已加载
|
||||
*/
|
||||
export const createDynamicKeymapExtension = async (): Promise<Extension> => {
|
||||
const keybindingStore = useKeybindingStore()
|
||||
|
||||
// 确保快捷键配置已加载
|
||||
if (keybindingStore.keyBindings.length === 0) {
|
||||
await keybindingStore.loadKeyBindings()
|
||||
}
|
||||
|
||||
return KeymapManager.createKeymapExtension(keybindingStore.enabledKeyBindings)
|
||||
}
|
||||
|
||||
// 导出相关模块
|
||||
export { KeymapManager } from './keymapManager'
|
||||
export { commandRegistry, getCommandHandler, getCommandDescription, isCommandRegistered, getRegisteredCommands } from './commandRegistry'
|
||||
export type { KeyBinding, CommandHandler, CommandDefinition, KeymapResult } from './types'
|
||||
84
frontend/src/views/editor/extensions/keymap/keymapManager.ts
Normal file
84
frontend/src/views/editor/extensions/keymap/keymapManager.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import {keymap} from '@codemirror/view'
|
||||
import {Extension} from '@codemirror/state'
|
||||
import {KeyBinding as KeyBindingConfig} from '@/../bindings/voidraft/internal/models/models'
|
||||
import {KeyBinding, KeymapResult} from './types'
|
||||
import {getCommandHandler, isCommandRegistered} from './commandRegistry'
|
||||
|
||||
/**
|
||||
* 快捷键管理器
|
||||
* 负责将后端配置转换为CodeMirror快捷键扩展
|
||||
*/
|
||||
export class KeymapManager {
|
||||
/**
|
||||
* 将后端快捷键配置转换为CodeMirror快捷键绑定
|
||||
* @param keyBindings 后端快捷键配置列表
|
||||
* @returns 转换结果
|
||||
*/
|
||||
static convertToKeyBindings(keyBindings: KeyBindingConfig[]): KeymapResult {
|
||||
const result: KeyBinding[] = []
|
||||
|
||||
for (const binding of keyBindings) {
|
||||
// 跳过禁用的快捷键
|
||||
if (!binding.enabled) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查命令是否已注册
|
||||
if (!isCommandRegistered(binding.command)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取命令处理函数
|
||||
const handler = getCommandHandler(binding.command)
|
||||
if (!handler) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 转换为CodeMirror快捷键格式
|
||||
const keyBinding: KeyBinding = {
|
||||
key: binding.key,
|
||||
run: handler,
|
||||
preventDefault: true
|
||||
}
|
||||
|
||||
result.push(keyBinding)
|
||||
}
|
||||
|
||||
return {keyBindings: result}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建CodeMirror快捷键扩展
|
||||
* @param keyBindings 后端快捷键配置列表
|
||||
* @returns CodeMirror扩展
|
||||
*/
|
||||
static createKeymapExtension(keyBindings: KeyBindingConfig[]): Extension {
|
||||
const {keyBindings: cmKeyBindings} =
|
||||
this.convertToKeyBindings(keyBindings)
|
||||
|
||||
return keymap.of(cmKeyBindings)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证快捷键配置
|
||||
* @param keyBindings 快捷键配置列表
|
||||
* @returns 验证结果
|
||||
*/
|
||||
static validateKeyBindings(keyBindings: KeyBindingConfig[]): {
|
||||
valid: KeyBindingConfig[]
|
||||
invalid: KeyBindingConfig[]
|
||||
} {
|
||||
const valid: KeyBindingConfig[] = []
|
||||
const invalid: KeyBindingConfig[] = []
|
||||
|
||||
for (const binding of keyBindings) {
|
||||
if (binding.enabled && binding.key && isCommandRegistered(binding.command)) {
|
||||
valid.push(binding)
|
||||
} else {
|
||||
invalid.push(binding)
|
||||
}
|
||||
}
|
||||
|
||||
return {valid, invalid}
|
||||
}
|
||||
}
|
||||
30
frontend/src/views/editor/extensions/keymap/types.ts
Normal file
30
frontend/src/views/editor/extensions/keymap/types.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {Command} from '@codemirror/view'
|
||||
|
||||
/**
|
||||
* CodeMirror快捷键绑定格式
|
||||
*/
|
||||
export interface KeyBinding {
|
||||
key: string
|
||||
run: Command
|
||||
preventDefault?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 命令处理函数类型
|
||||
*/
|
||||
export type CommandHandler = Command
|
||||
|
||||
/**
|
||||
* 命令定义接口
|
||||
*/
|
||||
export interface CommandDefinition {
|
||||
handler: CommandHandler
|
||||
descriptionKey: string // 翻译键
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷键转换结果
|
||||
*/
|
||||
export interface KeymapResult {
|
||||
keyBindings: KeyBinding[]
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
export { VSCodeSearch, vscodeSearch} from "./plugin";
|
||||
export { searchVisibilityField, SearchVisibilityEffect } from "./state";
|
||||
export { customSearchKeymap } from "./keymap";
|
||||
export { searchBaseTheme } from "./theme"
|
||||
export * from "./commands"
|
||||
@@ -1,81 +0,0 @@
|
||||
import { KeyBinding } from "@codemirror/view";
|
||||
import { deleteCharacterBackwards, deleteCharacterFowards, hideSearchVisibilityCommand, searchFindPrevious, searchFindReplaceMatch, searchMoveCursorLeft, searchMoveCursorRight, searchReplaceAll, searchShowReplace, searchToggleCase, searchToggleRegex, searchToggleWholeWord, selectAllCommand, showSearchVisibilityCommand } from "./commands";
|
||||
|
||||
export const customSearchKeymap: KeyBinding[] = [
|
||||
// 全局快捷键 - 不需要 scope 限制
|
||||
{
|
||||
key: 'Mod-f',
|
||||
run: showSearchVisibilityCommand,
|
||||
},
|
||||
// 添加备用快捷键绑定,确保兼容性
|
||||
{
|
||||
key: 'Ctrl-f',
|
||||
run: showSearchVisibilityCommand,
|
||||
},
|
||||
// 搜索面板内的快捷键 - 需要 scope 限制
|
||||
{
|
||||
key: 'Mod-a',
|
||||
run: selectAllCommand,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Escape',
|
||||
run: hideSearchVisibilityCommand,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Alt-c',
|
||||
run: searchToggleCase,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Alt-w',
|
||||
run: searchToggleWholeWord,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Alt-r',
|
||||
run: searchToggleRegex,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Mod-h',
|
||||
run: searchShowReplace,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Enter',
|
||||
run: searchFindReplaceMatch,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Shift-Enter',
|
||||
run: searchFindPrevious,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Mod-Alt-Enter',
|
||||
run: searchReplaceAll,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Backspace',
|
||||
run: deleteCharacterBackwards,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: 'Delete',
|
||||
run: deleteCharacterFowards,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: "ArrowLeft",
|
||||
run: searchMoveCursorLeft,
|
||||
scope: 'search'
|
||||
},
|
||||
{
|
||||
key: "ArrowRight",
|
||||
run: searchMoveCursorRight,
|
||||
scope: 'search'
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user