You can add strings to your custom dictionary (right click on word checked and add).
I use this .ts script for searching:
import { App, Plugin, Modal, Setting } from 'obsidian';
class FormattingOptionsModal extends Modal {
private originalSelection: string;
private onSubmit: (searchTerm: string) => void;
private hasBold: boolean;
private hasBackticks: boolean;
constructor(app: App, selection: string, onSubmit: (searchTerm: string) => void) {
super(app);
this.originalSelection = selection;
this.onSubmit = onSubmit;
this.hasBold = /\*\*[^*]+\*\*/.test(this.originalSelection);
this.hasBackticks = /`[^`]+`/.test(this.originalSelection);
}
onOpen() {
const { contentEl } = this;
contentEl.createEl('h2', { text: 'Search Options' });
contentEl.createEl('p', { text: 'Your selection contains markdown formatting. Choose search options:' });
let makeBoldOptional = false;
let makeBackticksOptional = false;
if (this.hasBold) {
new Setting(contentEl)
.setName('Make bold formatting optional')
.setDesc('Search for text with or without **bold** formatting')
.addToggle(toggle => toggle.onChange(value => makeBoldOptional = value));
}
if (this.hasBackticks) {
new Setting(contentEl)
.setName('Make backticks optional')
.setDesc('Search for text with or without `backtick` formatting')
.addToggle(toggle => toggle.onChange(value => makeBackticksOptional = value));
}
new Setting(contentEl)
.addButton(btn => btn
.setButtonText('Search')
.setCta()
.onClick(() => {
let searchTerm = this.originalSelection;
console.log('Original selection:', searchTerm);
if (!searchTerm) {
console.warn('Empty selection passed to modal');
this.close();
return;
}
// Handle bold formatting with optional toggle
if (this.hasBold) {
if (makeBoldOptional) {
searchTerm = searchTerm.replace(/\*\*(.+?)\*\*/g, (_, inner) =>
`(\\*\\*${escapeRegex(inner)}\\*\\*|${escapeRegex(inner)})`
);
console.log('After bold optional:', searchTerm);
} else {
searchTerm = searchTerm.replace(/\*\*(.+?)\*\*/g, (_, inner) =>
`\\*\\*${escapeRegex(inner)}\\*\\*`
);
console.log('After bold strict:', searchTerm);
}
}
// Handle backticks formatting with optional toggle
if (this.hasBackticks) {
if (makeBackticksOptional) {
searchTerm = searchTerm.replace(/`(.+?)`/g, (_, inner) =>
`(\`${escapeRegex(inner)}\`|${escapeRegex(inner)})`
);
console.log('After backticks optional:', searchTerm);
} else {
searchTerm = searchTerm.replace(/`(.+?)`/g, (_, inner) =>
`\`${escapeRegex(inner)}\``
);
console.log('After backticks strict:', searchTerm);
}
}
// DO NOT escape the whole searchTerm again here!
// It already contains regex groups for optional formatting.
const finalSearchTerm = '/' + searchTerm;
console.log('Final search term (modal):', finalSearchTerm);
this.onSubmit(finalSearchTerm);
this.close();
}))
.addButton(btn => btn
.setButtonText('Cancel')
.onClick(() => this.close()));
}
onClose() {
this.contentEl.empty();
}
}
const searchGlobally = async (app: App): Promise<void> => {
const activeLeaf = app.workspace.getMostRecentLeaf();
const editor = activeLeaf?.view?.editor;
if (!editor) {
window.open('obsidian://search?query=/');
return;
}
const rawSelection = editor.getSelection();
const selection = typeof rawSelection === 'string' ? rawSelection.trim() : '';
console.log('[Plugin] Selection from editor:', selection);
if (!selection) {
window.open('obsidian://search?query=/');
return;
}
const isAlreadyRegex = (text: string): boolean => /^\/.*\/[gimsuy]*$/.test(text);
const hasPrefix = (text: string): boolean =>
['file: ', 'path: ', 'tag: ', 'section: ', 'content: ', 'line: '].some(prefix => text.startsWith(prefix));
const isYYMMDDFormat = (text: string): boolean =>
/^\d{6}$/.test(text) && (() => {
const y = parseInt(text.slice(0, 2));
const m = parseInt(text.slice(2, 4));
const d = parseInt(text.slice(4, 6));
return m >= 1 && m <= 12 && d >= 1 && d <= 31;
})();
const hasMarkdownFormatting = (text: string): boolean =>
/\*\*[^*]+\*\*/.test(text) || /`[^`]+`/.test(text);
const escapeAndSearch = (query: string) => {
window.open(`obsidian://search?query=${encodeURIComponent(query)}`);
};
if (hasPrefix(selection)) {
escapeAndSearch(selection);
return;
}
if (isYYMMDDFormat(selection)) {
const escaped = escapeRegex(selection);
escapeAndSearch(`file:"Collections Dashboard.canvas" /${escaped}`);
return;
}
if (hasMarkdownFormatting(selection)) {
// Open modal for optional markdown search
new FormattingOptionsModal(app, selection, (searchTerm: string) => {
escapeAndSearch(searchTerm);
}).open();
return;
}
// No markdown formatting detected: fully escape for strict literal regex
const escaped = escapeRegex(selection);
escapeAndSearch('/' + escaped);
};
function escapeRegex(text: string): string {
// Escapes all special regex chars (including /) so that they are treated literally
return text.replace(/[.*+?^${}()|[\]\\\/]/g, '\\$&');
}
export default class SearchGloballyPlugin extends Plugin {
async onload() {
this.addCommand({
id: 'search-globally-custom',
name: 'Search Globally Custom',
callback: () => searchGlobally(this.app)
});
}
}
export async function invoke(app: App): Promise<void> {
return searchGlobally(app);
}
Save it as ‘Search-Globally.ts’ and register it (well, not it, but the folder you placed it in) with Codescript Toolkit.
Assign a key combo to it and when you have text selected in your markdown, fire it.
The basic functionality is using regex for everything and if you have markdown formatting such as bold or inline code, in a modal it will make you choose what to search with (possibly both versions, if you pick that).