diff --git a/main.ts b/main.ts index 956a6b5..6309d16 100644 --- a/main.ts +++ b/main.ts @@ -1,8 +1,12 @@ -import {App, Plugin, MarkdownView, Notice, WorkspaceLeaf, setIcon, Keymap, setTooltip} from 'obsidian'; +import {Plugin, MarkdownView, Notice, WorkspaceLeaf, setIcon, Keymap, setTooltip} from 'obsidian'; import {Compartment} from '@codemirror/state'; import {EditorView} from '@codemirror/view'; import {OccuraPluginSettingTab, OccuraPluginSettings, DEFAULT_SETTINGS} from 'src/settings' -import {highlightOccurrenceExtension} from 'src/highlighter' +import { + highlightOccurrenceExtension, + setHighlightOccurrences, + removeHighlightOccurrences +} from 'src/highlighter' import {parseHotkeyString} from 'src/utils' export default class OccuraPlugin extends Plugin { @@ -39,6 +43,30 @@ export default class OccuraPlugin extends Plugin { } }); + this.addCommand({ + id: 'set-permanent-highlight-occurrences', + name: 'Set permanently highlight for occurrences', + callback: () => { + if(this.settings.occuraPluginEnabled ){ + setHighlightOccurrences(this); + } else { + new Notice('Please enable Occura'); + } + }, + }); + + this.addCommand({ + id: 'remove-permanent-highlight-occurrences', + name: 'Remove permanently highlight for occurrences', + callback: () => { + if(this.settings.occuraPluginEnabled ){ + removeHighlightOccurrences(this); + } else { + new Notice('Please enable Occura'); + } + }, + }); + // Add icon to the editor title bar when a new leaf is created this.registerEvent( this.app.workspace.on('layout-change', () => { @@ -220,6 +248,9 @@ export default class OccuraPlugin extends Plugin { const icons = document.querySelectorAll('.highlight-toggle-icon'); icons.forEach(icon => icon.remove()); } + + + } diff --git a/manifest.json b/manifest.json index 0873d41..fdf17e4 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "occura-word-highlighter", "name": "Occura", - "version": "1.0.0", + "version": "1.1.0", "minAppVersion": "0.15.0", "description": "Find and highlight all occurrences of selected text in notes, similar to Notepad++ or IDEs.", "author": "Alexey Sedoykin", diff --git a/package.json b/package.json index 5511f42..accc2a5 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,16 @@ { - "name": "occura-plugin", + "name": "obsidian-occura-plugin", "version": "1.0.0", "description": "Highlighting all occurrences of the selected word, similar to Notepad++ or any IDE.", "main": "main.js", "scripts": { "dev": "node esbuild.config.mjs", "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", - "version": "node version-bump.mjs && git add manifest.json versions.json" + "version": "node version-bump.mjs && git add manifest.json versions.json", + + "copy-plugin": "rsync -a --delete ./ \"/Users/doykin/_ObsidianNotes_/.obsidian/plugins/$npm_package_name/\"", + "dev-in-plugin": "cd \"/Users/doykin/_ObsidianNotes_/.obsidian/plugins/$npm_package_name/\" && npm run dev", + "dev:copy": "npm run copy-plugin && npm run dev-in-plugin" }, "keywords": [], "author": "", diff --git a/src/highlighter.ts b/src/highlighter.ts index ac4c891..dac973c 100644 --- a/src/highlighter.ts +++ b/src/highlighter.ts @@ -3,6 +3,7 @@ import {EditorView, Decoration, DecorationSet, ViewUpdate, ViewPlugin} from '@codemirror/view'; import {RangeSetBuilder} from '@codemirror/state'; import type OccuraPlugin from 'main'; +import {MarkdownView, Notice} from "obsidian"; // Create a decoration for highlighting export const highlightDecoration = Decoration.mark({class: 'found-occurrence'}); @@ -82,3 +83,116 @@ export function highlightOccurrenceExtension(plugin: OccuraPlugin) { ); } + +//region set/remove permanent highlighting +export function setHighlightOccurrences(context:any) { + const activeView = context.app.workspace.getActiveViewOfType(MarkdownView); + if (!activeView) { + new Notice('No active editor'); + return; + } + + const editor = activeView.editor; + const selectedText = editor.getSelection().trim(); + + if (!selectedText || /\s/.test(selectedText)) { + new Notice('Please select some text to highlight.'); + return; + } + + // Escape regex special characters + const escapedText = selectedText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const regex = new RegExp(escapedText, 'g'); + + const docText = editor.getValue(); + const matches: { from: number; to: number }[] = []; + + let match; + while ((match = regex.exec(docText)) !== null) { + matches.push({ from: match.index, to: match.index + match[0].length }); + } + + if (matches.length === 0) { + new Notice('No occurrences found.'); + return; + } + + // Access the underlying EditorView + const editorView = (editor as any).cm as EditorView; + if (!editorView) { + new Notice('Cannot access the editor view.'); + return; + } + + // Prepare changes + const changes = matches.reverse().map(range => ({ + from: range.from, + to: range.to, + insert: `==${docText.slice(range.from, range.to)}==`, + })); + + // Apply all changes in a single transaction + editorView.dispatch({ + changes, + }); + + new Notice(`Permanently highlighted ${matches.length} for ${selectedText} occurrences.`); +} +export function removeHighlightOccurrences(context:any) { + const activeView = context.app.workspace.getActiveViewOfType(MarkdownView); + if (!activeView) { + new Notice('No active editor'); + return; + } + + const editor = activeView.editor; + const selectedText = editor.getSelection().trim(); + + if (!selectedText || /\s/.test(selectedText)) { + new Notice('Please select some text to remove highlighting from.'); + return; + } + + // Construct the search pattern to find ==selectedText== + const escapedText = selectedText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const pattern = `==${escapedText}==`; + const regex = new RegExp(pattern, 'g'); + + const docText = editor.getValue(); + const matches: { from: number; to: number }[] = []; + + let match; + while ((match = regex.exec(docText)) !== null) { + matches.push({ from: match.index, to: match.index + match[0].length }); + } + + if (matches.length === 0) { + new Notice('No highlighted occurrences found.'); + return; + } + + // Access the underlying EditorView + const editorView = (editor as any).cm as EditorView; + if (!editorView) { + new Notice('Cannot access the editor view.'); + return; + } + + // Prepare changes + const changes = matches.reverse().map(range => { + const originalText = docText.slice(range.from + 2, range.to - 2); // Remove the '==' from both ends + return { + from: range.from, + to: range.to, + insert: originalText, + }; + }); + + // Apply all changes in a single transaction + editorView.dispatch({ + changes, + }); + + new Notice(`Removed highlighting from ${matches.length} occurrences of ${selectedText}.`); +} +//endregion \ No newline at end of file diff --git a/versions.json b/versions.json index 26382a1..df3d877 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "1.0.0": "0.15.0" + "1.1.0": "0.15.0" }