Added cursor protection extension

This commit is contained in:
2025-11-13 21:00:35 +08:00
parent d42f913250
commit dec3ef5ef4
3 changed files with 211 additions and 23 deletions

View File

@@ -0,0 +1,123 @@
/**
* 光标保护扩展
* 防止光标通过方向键移动到分隔符区域
*/
import { EditorView } from '@codemirror/view';
import { EditorSelection } from '@codemirror/state';
import { blockState } from './state';
/**
* 检查位置是否在分隔符区域内
*/
function isInDelimiter(view: EditorView, pos: number): boolean {
try {
const blocks = view.state.field(blockState, false);
if (!blocks) return false;
for (const block of blocks) {
if (pos >= block.delimiter.from && pos < block.delimiter.to) {
return true;
}
}
return false;
} catch {
return false;
}
}
/**
* 调整光标位置,跳过分隔符区域
*/
function adjustPosition(view: EditorView, pos: number, forward: boolean): number {
try {
const blocks = view.state.field(blockState, false);
if (!blocks || blocks.length === 0) return pos;
for (const block of blocks) {
// 如果位置在分隔符内
if (pos >= block.delimiter.from && pos < block.delimiter.to) {
// 向前移动:跳到该块内容的开始
// 向后移动:跳到前一个块的内容末尾
if (forward) {
return block.content.from;
} else {
// 找到前一个块
const blockIndex = blocks.indexOf(block);
if (blockIndex > 0) {
const prevBlock = blocks[blockIndex - 1];
return prevBlock.content.to;
}
return block.delimiter.from;
}
}
}
return pos;
} catch {
return pos;
}
}
/**
* 光标保护扩展
* 拦截方向键移动,防止光标进入分隔符区域
*/
export function createCursorProtection() {
return EditorView.domEventHandlers({
keydown(event, view) {
// 只处理方向键
if (!['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key)) {
return false;
}
// 获取当前光标位置
const selection = view.state.selection.main;
const currentPos = selection.head;
// 计算目标位置
let targetPos = currentPos;
if (event.key === 'ArrowLeft') {
targetPos = Math.max(0, currentPos - 1);
} else if (event.key === 'ArrowRight') {
targetPos = Math.min(view.state.doc.length, currentPos + 1);
} else if (event.key === 'ArrowUp') {
const line = view.state.doc.lineAt(currentPos);
if (line.number > 1) {
const prevLine = view.state.doc.line(line.number - 1);
const col = currentPos - line.from;
targetPos = Math.min(prevLine.from + col, prevLine.to);
}
} else if (event.key === 'ArrowDown') {
const line = view.state.doc.lineAt(currentPos);
if (line.number < view.state.doc.lines) {
const nextLine = view.state.doc.line(line.number + 1);
const col = currentPos - line.from;
targetPos = Math.min(nextLine.from + col, nextLine.to);
}
}
// 检查目标位置是否在分隔符内
if (isInDelimiter(view, targetPos)) {
// 调整位置
const forward = event.key === 'ArrowRight' || event.key === 'ArrowDown';
const adjustedPos = adjustPosition(view, targetPos, forward);
// 移动光标到调整后的位置
view.dispatch({
selection: EditorSelection.cursor(adjustedPos),
scrollIntoView: true,
userEvent: 'select'
});
// 阻止默认行为
event.preventDefault();
return true;
}
return false;
}
});
}

View File

@@ -25,6 +25,7 @@ import {getCodeBlockLanguageExtension} from './lang-parser';
import {createLanguageDetection} from './lang-detect';
import {SupportedLanguage} from './types';
import {getMathBlockExtensions} from './mathBlock';
import {createCursorProtection} from './cursorProtection';
/**
* 代码块扩展配置选项
@@ -108,6 +109,9 @@ export function createCodeBlockExtension(options: CodeBlockOptions = {}): Extens
showBackground
}),
// 光标保护(防止方向键移动到分隔符上)
createCursorProtection(),
// 块选择功能
...getBlockSelectExtensions(),
@@ -207,6 +211,11 @@ export {
getMathBlockExtensions
} from './mathBlock';
// 光标保护功能
export {
createCursorProtection
} from './cursorProtection';
/**
* 默认导出
*/