Skip to content

Commit bdcf304

Browse files
committed
Has been added the new command:
1.Set permanently highlight for occurrences 2.Remove permanently highlight for occurrences
1 parent 6f3e128 commit bdcf304

File tree

5 files changed

+155
-6
lines changed

5 files changed

+155
-6
lines changed

main.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import {App, Plugin, MarkdownView, Notice, WorkspaceLeaf, setIcon, Keymap, setTooltip} from 'obsidian';
1+
import {Plugin, MarkdownView, Notice, WorkspaceLeaf, setIcon, Keymap, setTooltip} from 'obsidian';
22
import {Compartment} from '@codemirror/state';
33
import {EditorView} from '@codemirror/view';
44
import {OccuraPluginSettingTab, OccuraPluginSettings, DEFAULT_SETTINGS} from 'src/settings'
5-
import {highlightOccurrenceExtension} from 'src/highlighter'
5+
import {
6+
highlightOccurrenceExtension,
7+
setHighlightOccurrences,
8+
removeHighlightOccurrences
9+
} from 'src/highlighter'
610
import {parseHotkeyString} from 'src/utils'
711

812
export default class OccuraPlugin extends Plugin {
@@ -39,6 +43,30 @@ export default class OccuraPlugin extends Plugin {
3943
}
4044
});
4145

46+
this.addCommand({
47+
id: 'set-permanent-highlight-occurrences',
48+
name: 'Set permanently highlight for occurrences',
49+
callback: () => {
50+
if(this.settings.occuraPluginEnabled ){
51+
setHighlightOccurrences(this);
52+
} else {
53+
new Notice('Please enable Occura');
54+
}
55+
},
56+
});
57+
58+
this.addCommand({
59+
id: 'remove-permanent-highlight-occurrences',
60+
name: 'Remove permanently highlight for occurrences',
61+
callback: () => {
62+
if(this.settings.occuraPluginEnabled ){
63+
removeHighlightOccurrences(this);
64+
} else {
65+
new Notice('Please enable Occura');
66+
}
67+
},
68+
});
69+
4270
// Add icon to the editor title bar when a new leaf is created
4371
this.registerEvent(
4472
this.app.workspace.on('layout-change', () => {
@@ -220,6 +248,9 @@ export default class OccuraPlugin extends Plugin {
220248
const icons = document.querySelectorAll('.highlight-toggle-icon');
221249
icons.forEach(icon => icon.remove());
222250
}
251+
252+
253+
223254
}
224255

225256

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "occura-word-highlighter",
33
"name": "Occura",
4-
"version": "1.0.0",
4+
"version": "1.1.0",
55
"minAppVersion": "0.15.0",
66
"description": "Find and highlight all occurrences of selected text in notes, similar to Notepad++ or IDEs.",
77
"author": "Alexey Sedoykin",

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
{
2-
"name": "occura-plugin",
2+
"name": "obsidian-occura-plugin",
33
"version": "1.0.0",
44
"description": "Highlighting all occurrences of the selected word, similar to Notepad++ or any IDE.",
55
"main": "main.js",
66
"scripts": {
77
"dev": "node esbuild.config.mjs",
88
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
9-
"version": "node version-bump.mjs && git add manifest.json versions.json"
9+
"version": "node version-bump.mjs && git add manifest.json versions.json",
10+
11+
"copy-plugin": "rsync -a --delete ./ \"/Users/doykin/_ObsidianNotes_/.obsidian/plugins/$npm_package_name/\"",
12+
"dev-in-plugin": "cd \"/Users/doykin/_ObsidianNotes_/.obsidian/plugins/$npm_package_name/\" && npm run dev",
13+
"dev:copy": "npm run copy-plugin && npm run dev-in-plugin"
1014
},
1115
"keywords": [],
1216
"author": "",

src/highlighter.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import {EditorView, Decoration, DecorationSet, ViewUpdate, ViewPlugin} from '@codemirror/view';
44
import {RangeSetBuilder} from '@codemirror/state';
55
import type OccuraPlugin from 'main';
6+
import {MarkdownView, Notice} from "obsidian";
67

78
// Create a decoration for highlighting
89
export const highlightDecoration = Decoration.mark({class: 'found-occurrence'});
@@ -82,3 +83,116 @@ export function highlightOccurrenceExtension(plugin: OccuraPlugin) {
8283
);
8384

8485
}
86+
87+
//region set/remove permanent highlighting
88+
export function setHighlightOccurrences(context:any) {
89+
const activeView = context.app.workspace.getActiveViewOfType(MarkdownView);
90+
if (!activeView) {
91+
new Notice('No active editor');
92+
return;
93+
}
94+
95+
const editor = activeView.editor;
96+
const selectedText = editor.getSelection().trim();
97+
98+
if (!selectedText || /\s/.test(selectedText)) {
99+
new Notice('Please select some text to highlight.');
100+
return;
101+
}
102+
103+
// Escape regex special characters
104+
const escapedText = selectedText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
105+
const regex = new RegExp(escapedText, 'g');
106+
107+
const docText = editor.getValue();
108+
const matches: { from: number; to: number }[] = [];
109+
110+
let match;
111+
while ((match = regex.exec(docText)) !== null) {
112+
matches.push({ from: match.index, to: match.index + match[0].length });
113+
}
114+
115+
if (matches.length === 0) {
116+
new Notice('No occurrences found.');
117+
return;
118+
}
119+
120+
// Access the underlying EditorView
121+
const editorView = (editor as any).cm as EditorView;
122+
if (!editorView) {
123+
new Notice('Cannot access the editor view.');
124+
return;
125+
}
126+
127+
// Prepare changes
128+
const changes = matches.reverse().map(range => ({
129+
from: range.from,
130+
to: range.to,
131+
insert: `==${docText.slice(range.from, range.to)}==`,
132+
}));
133+
134+
// Apply all changes in a single transaction
135+
editorView.dispatch({
136+
changes,
137+
});
138+
139+
new Notice(`Permanently highlighted ${matches.length} for ${selectedText} occurrences.`);
140+
}
141+
export function removeHighlightOccurrences(context:any) {
142+
const activeView = context.app.workspace.getActiveViewOfType(MarkdownView);
143+
if (!activeView) {
144+
new Notice('No active editor');
145+
return;
146+
}
147+
148+
const editor = activeView.editor;
149+
const selectedText = editor.getSelection().trim();
150+
151+
if (!selectedText || /\s/.test(selectedText)) {
152+
new Notice('Please select some text to remove highlighting from.');
153+
return;
154+
}
155+
156+
// Construct the search pattern to find ==selectedText==
157+
const escapedText = selectedText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
158+
const pattern = `==${escapedText}==`;
159+
const regex = new RegExp(pattern, 'g');
160+
161+
const docText = editor.getValue();
162+
const matches: { from: number; to: number }[] = [];
163+
164+
let match;
165+
while ((match = regex.exec(docText)) !== null) {
166+
matches.push({ from: match.index, to: match.index + match[0].length });
167+
}
168+
169+
if (matches.length === 0) {
170+
new Notice('No highlighted occurrences found.');
171+
return;
172+
}
173+
174+
// Access the underlying EditorView
175+
const editorView = (editor as any).cm as EditorView;
176+
if (!editorView) {
177+
new Notice('Cannot access the editor view.');
178+
return;
179+
}
180+
181+
// Prepare changes
182+
const changes = matches.reverse().map(range => {
183+
const originalText = docText.slice(range.from + 2, range.to - 2); // Remove the '==' from both ends
184+
return {
185+
from: range.from,
186+
to: range.to,
187+
insert: originalText,
188+
};
189+
});
190+
191+
// Apply all changes in a single transaction
192+
editorView.dispatch({
193+
changes,
194+
});
195+
196+
new Notice(`Removed highlighting from ${matches.length} occurrences of ${selectedText}.`);
197+
}
198+
//endregion

versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"1.0.0": "0.15.0"
2+
"1.1.0": "0.15.0"
33
}

0 commit comments

Comments
 (0)