feat: select next occurrence
Some checks failed
Node.js build / build (20.x) (push) Has been cancelled
Node.js build / build (22.x) (push) Has been cancelled

This commit is contained in:
2026-02-25 11:35:15 -05:00
parent 28ebf6a4a8
commit efbd5773b5
2 changed files with 77 additions and 1 deletions

View File

@@ -560,6 +560,8 @@ export default class BindThemPlugin extends Plugin {
this.addConditionalCommand({ id: 'go-to-next-word', name: 'Go to next word', icon: 'arrow-right', editorCallback: (e) => e.exec('goWordRight') });
this.addConditionalCommand({ id: 'insert-cursor-above', name: 'Insert cursor above', icon: 'plus-circle', editorCallback: (e) => this.insertCursor(e, -1) });
this.addConditionalCommand({ id: 'insert-cursor-below', name: 'Insert cursor below', icon: 'plus-circle', editorCallback: (e) => this.insertCursor(e, 1) });
this.addConditionalCommand({ id: 'select-next-occurrence', name: 'Select next occurrence', icon: 'text-cursor', editorCallback: (e) => this.selectOccurrence(e, 'next') });
this.addConditionalCommand({ id: 'select-prev-occurrence', name: 'Select previous occurrence', icon: 'text-cursor', editorCallback: (e) => this.selectOccurrence(e, 'prev') });
// File Operations
this.addConditionalCommand({ id: 'duplicate-file', name: 'Duplicate file', icon: 'copy', editorCallback: (e, v) => { if (v instanceof MarkdownView) void this.duplicateFile(v); } });
@@ -641,6 +643,78 @@ export default class BindThemPlugin extends Plugin {
editor.setSelections([...editor.listSelections(), ...newSels]);
}
private selectOccurrence(editor: Editor, direction: 'next' | 'prev'): void {
const selections = editor.listSelections();
if (selections.length === 0) return;
// Get the selected text from the last selection (or first for prev)
const lastSelection = direction === 'next' ? selections[selections.length - 1] : selections[0];
const range = selectionToRange(lastSelection);
const selectedText = editor.getRange(range.from, range.to);
// If no text is selected, select the word under cursor first
if (!selectedText) {
this.selectWord(editor);
return;
}
// Get the entire document content
const fullText = editor.getValue();
// Find all occurrences of the selected text
const occurrences: { from: number; to: number }[] = [];
let searchPos = 0;
while (true) {
const idx = fullText.indexOf(selectedText, searchPos);
if (idx === -1) break;
occurrences.push({ from: idx, to: idx + selectedText.length });
searchPos = idx + 1;
}
if (occurrences.length <= 1) return; // No other occurrences
// Convert current selection to offset
const currentOffset = editor.posToOffset(range.from);
// Find the next/previous occurrence
let targetOccurrence: { from: number; to: number } | null = null;
if (direction === 'next') {
// Find first occurrence after current position
for (const occ of occurrences) {
if (occ.from > currentOffset) {
targetOccurrence = occ;
break;
}
}
// Wrap around to first occurrence if not found
if (!targetOccurrence) {
targetOccurrence = occurrences[0];
}
} else {
// Find first occurrence before current position
for (let i = occurrences.length - 1; i >= 0; i--) {
if (occurrences[i].from < currentOffset) {
targetOccurrence = occurrences[i];
break;
}
}
// Wrap around to last occurrence if not found
if (!targetOccurrence) {
targetOccurrence = occurrences[occurrences.length - 1];
}
}
if (targetOccurrence) {
const from = editor.offsetToPos(targetOccurrence.from);
const to = editor.offsetToPos(targetOccurrence.to);
// Add new selection to existing selections
const newSelections = [...selections, { anchor: from, head: to }];
editor.setSelections(newSelections);
}
}
private goToHeading(editor: Editor, direction: 'next' | 'prev'): void {
const file = this.app.workspace.getActiveFile();
const cache = file ? this.app.metadataCache.getFileCache(file) : null;

View File

@@ -104,7 +104,9 @@ export const COMMANDS: Record<keyof typeof COMMAND_CATEGORIES, CommandDefinition
{ id: 'go-to-prev-word', name: 'Go to previous word' },
{ id: 'go-to-next-word', name: 'Go to next word' },
{ id: 'insert-cursor-above', name: 'Insert cursor above' },
{ id: 'insert-cursor-below', name: 'Insert cursor below' }
{ id: 'insert-cursor-below', name: 'Insert cursor below' },
{ id: 'select-next-occurrence', name: 'Select next occurrence' },
{ id: 'select-prev-occurrence', name: 'Select previous occurrence' }
],
fileOperations: [
{ id: 'duplicate-file', name: 'Duplicate file' },