370 lines
30 KiB
JavaScript
370 lines
30 KiB
JavaScript
'use strict';
|
|
|
|
var obsidian = require('obsidian');
|
|
var state = require('@codemirror/state');
|
|
var view = require('@codemirror/view');
|
|
var language = require('@codemirror/language');
|
|
|
|
class SettingsTab extends obsidian.PluginSettingTab {
|
|
plugin;
|
|
constructor(app, plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
arrayMove(arr, fromIndex, toIndex) {
|
|
if (toIndex < 0 || toIndex === arr.length) {
|
|
return;
|
|
}
|
|
const element = arr[fromIndex];
|
|
arr[fromIndex] = arr[toIndex];
|
|
arr[toIndex] = element;
|
|
}
|
|
addRegexSettings() {
|
|
let desc = document.createDocumentFragment();
|
|
desc.append('Custom Regex for specifying patterns to conceal/replace.', desc.createEl('br'), 'Check the ', desc.createEl('a', {
|
|
href: 'https://github.com/mattcoleanderson/obsidian-dynamic-text-concealer/discussions/19',
|
|
text: 'List of community made regex',
|
|
}), ' Discussion for working examples. Feel free to add your own as well.');
|
|
new obsidian.Setting(this.containerEl).setHeading().setName('Regular Expressions').setDesc(desc);
|
|
this.plugin.settings.regexp.forEach((regex, index) => {
|
|
const setting = new obsidian.Setting(this.containerEl)
|
|
.addText((text) => {
|
|
text.setValue(regex).onChange((newRegex) => {
|
|
if (newRegex && this.plugin.settings.regexp.contains(newRegex)) {
|
|
// TODO: Log Error
|
|
return;
|
|
}
|
|
this.plugin.settings.regexp[index] = newRegex;
|
|
this.plugin.saveSettings();
|
|
this.plugin.updateEditorExtension();
|
|
});
|
|
})
|
|
.addExtraButton((button) => {
|
|
button
|
|
.setIcon('up-chevron-glyph')
|
|
.setTooltip('Move up')
|
|
.onClick(() => {
|
|
console.log('Oh No!');
|
|
this.arrayMove(this.plugin.settings.regexp, index, index - 1);
|
|
this.plugin.saveSettings();
|
|
this.display();
|
|
this.plugin.updateEditorExtension();
|
|
});
|
|
})
|
|
.addExtraButton((button) => {
|
|
button
|
|
.setIcon('down-chevron-glyph')
|
|
.setTooltip('Move down')
|
|
.onClick(() => {
|
|
this.arrayMove(this.plugin.settings.regexp, index, index + 1);
|
|
this.plugin.saveSettings();
|
|
this.display();
|
|
this.plugin.updateEditorExtension();
|
|
});
|
|
})
|
|
.addExtraButton((button) => {
|
|
button
|
|
.setIcon('cross')
|
|
.setTooltip('Delete')
|
|
.onClick(() => {
|
|
this.plugin.settings.regexp.splice(index, 1);
|
|
this.plugin.saveSettings();
|
|
this.plugin.updateEditorExtension();
|
|
this.display();
|
|
});
|
|
});
|
|
setting.infoEl.remove();
|
|
setting.controlEl.firstElementChild?.addClass('dtc-setting');
|
|
});
|
|
new obsidian.Setting(this.containerEl).addButton((button) => {
|
|
button
|
|
.setButtonText('Add new regular expression')
|
|
.setCta()
|
|
.onClick(() => {
|
|
this.plugin.settings.regexp.push('');
|
|
this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
});
|
|
}
|
|
async display() {
|
|
// This is the outtermost HTML element on the setting tab
|
|
const { containerEl } = this;
|
|
containerEl.empty();
|
|
// new Setting(containerEl)
|
|
// .setName('Conceal in Editing Mode')
|
|
// .setDesc(
|
|
// `Matched text is concealed in editing mode (live preview),
|
|
// except when cursor or selection overlaps with matched text.`,
|
|
// )
|
|
// .addToggle((toggle) =>
|
|
// toggle.setValue(this.plugin.settings.doConcealEditMode).onChange(async (value) => {
|
|
// this.plugin.settings.doConcealEditMode = value;
|
|
// await this.plugin.saveSettings();
|
|
// this.plugin.updateEditorExtension();
|
|
// }),
|
|
// );
|
|
// this.setupMatchTable();
|
|
this.addRegexSettings();
|
|
}
|
|
}
|
|
|
|
class ConcealMatchDecorator extends view.MatchDecorator {
|
|
lastSelectionFrom;
|
|
lastSelectionTo;
|
|
updateDeco(update, deco) {
|
|
let updateFrom;
|
|
let updateTo;
|
|
if (update.docChanged) {
|
|
({ updateFrom, updateTo } = this.updateChanges(update));
|
|
}
|
|
else if (update.selectionSet) {
|
|
({ updateFrom, updateTo } = this.updateSelection(update));
|
|
}
|
|
if (updateTo && updateFrom && updateTo - updateFrom <= 1000) {
|
|
return this['updateRange'](update.view, deco.map(update.changes), updateFrom, updateTo);
|
|
}
|
|
else if (update.viewportChanged) {
|
|
return this.createDeco(update.view);
|
|
}
|
|
return deco;
|
|
}
|
|
updateChanges(update) {
|
|
let updateFrom = 1e9;
|
|
let updateTo = -1;
|
|
update.changes.iterChanges((_f, _t, from, to) => {
|
|
if (to > update.view.viewport.from && from < update.view.viewport.to) {
|
|
updateFrom = update.state.doc.lineAt(Math.min(from, updateFrom)).from;
|
|
updateTo = update.state.doc.lineAt(Math.max(to, updateTo)).to;
|
|
}
|
|
});
|
|
return { updateFrom, updateTo };
|
|
}
|
|
/*
|
|
* updateSelection returns a range to update when a selection has been made
|
|
* to the document, suchas a moving the cursor of selecting multiple character and line
|
|
*/
|
|
updateSelection(update) {
|
|
const selection = update.state.selection.ranges;
|
|
// Get the earliest and latest positon of the lines in the selected range
|
|
let lineFrom = update.state.doc.lineAt(selection[0].from).from;
|
|
let lineTo = update.state.doc.lineAt(selection[selection.length - 1].to).to;
|
|
// Return the earliest and latest postions of the current and previous selection range
|
|
let updateFrom = Math.min(lineFrom, this.lastSelectionFrom);
|
|
let updateTo = Math.max(lineTo, this.lastSelectionTo);
|
|
// Retain the current selected range for the next update
|
|
this.lastSelectionFrom = lineFrom;
|
|
this.lastSelectionTo = lineTo;
|
|
return { updateFrom, updateTo };
|
|
}
|
|
}
|
|
|
|
class ConcealViewPlugin {
|
|
decorations; // list of current decorators in view
|
|
matchDecorator; // Creates and updates decorators
|
|
constructor(view$1, regexp) {
|
|
this.matchDecorator = new ConcealMatchDecorator({
|
|
regexp: regexp,
|
|
decorate: (add, from, to, match, view$1) => {
|
|
// Define conditions where a decorator should not be added for a match
|
|
if (this.isCodeblock(view$1, from, to))
|
|
return;
|
|
if (this.selectionAndRangeOverlap(view$1.state.selection, from, to))
|
|
return;
|
|
// Add mark decorator for each capture group in regex
|
|
for (let i = 1; i < match.length; i++) {
|
|
if (!match.indices)
|
|
continue;
|
|
// Call function to add decorator to DecorationSet for each capture group
|
|
const startPos = from + (match.indices[i][0] - match.index);
|
|
const finalPos = from + (match.indices[i][1] - match.index);
|
|
add(startPos, finalPos, view.Decoration.mark({ class: 'dtc-hide-match' }));
|
|
}
|
|
},
|
|
});
|
|
// Initialize the DecoratorSet if not in source mode
|
|
this.decorations = this.initializeDecorations(view$1);
|
|
}
|
|
/**
|
|
* isCodeblock returns true if the current current matches
|
|
* from and to position contains a code block
|
|
*/
|
|
isCodeblock(view, from, to) {
|
|
let isCodeblock = false;
|
|
language.syntaxTree(view.state).iterate({
|
|
from,
|
|
to,
|
|
enter: (node) => {
|
|
if (/^inline-code/.test(node.name) || node.name == 'HyperMD-codeblock_HyperMD-codeblock-bg') {
|
|
isCodeblock = true;
|
|
return false; // short circuit child iteration
|
|
}
|
|
},
|
|
});
|
|
return isCodeblock;
|
|
}
|
|
/**
|
|
* selectionAndRangeOverlap returns true if the specified range
|
|
* overlaps with the current cursor location or selection range
|
|
*/
|
|
selectionAndRangeOverlap(selection, rangeFrom, rangeTo) {
|
|
for (const range of selection.ranges) {
|
|
if (range.from <= rangeTo && range.to >= rangeFrom) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
update(update) {
|
|
const isSourceMode = !update.state.field(obsidian.editorLivePreviewField);
|
|
// TODO: Make this a state field
|
|
const isEditorLayoutChanged = update.transactions.some((t) => t.effects.some((e) => e.is(workspaceLayoutChangeEffect)));
|
|
// Reinitialize Decorations if sourc mode or recetly switch back to Live Preview
|
|
if (isSourceMode || isEditorLayoutChanged) {
|
|
this.decorations = this.initializeDecorations(update.view);
|
|
return;
|
|
}
|
|
// Update DecorationSet with MatchDecorator
|
|
this.decorations = this.matchDecorator.updateDeco(update, this.decorations);
|
|
}
|
|
destroy() { }
|
|
/**
|
|
* Initializes DecorationSet. Is disabled if the editor is in source mode.
|
|
*/
|
|
initializeDecorations(view$1) {
|
|
return view$1.state.field(obsidian.editorLivePreviewField) ? this.matchDecorator.createDeco(view$1) : view.Decoration.none;
|
|
}
|
|
}
|
|
const pluginSpec = {
|
|
decorations: (instance) => instance.decorations,
|
|
};
|
|
/**
|
|
* concealViewPlugin creates a ViewPlugin to be registers as an editorExtension
|
|
*/
|
|
const concealViewPlugin = (regexp) => {
|
|
return view.ViewPlugin.define((view) => new ConcealViewPlugin(view, regexp), pluginSpec);
|
|
};
|
|
/**
|
|
* A state effect that represents the workspace's layout change.
|
|
* Mainly intended to detect when the user switches between live preview and source mode.
|
|
*/
|
|
const workspaceLayoutChangeEffect = state.StateEffect.define();
|
|
|
|
class ConcealPostProcessor {
|
|
regexp;
|
|
ELEMENTS_TO_PROCESS = 'p, li';
|
|
REGEX_CURLY_REPLACEMENT = '$<answer>'; // first capture group; content is not concealed
|
|
constructor(regexp) {
|
|
this.regexp = regexp;
|
|
}
|
|
conceal = (element) => {
|
|
// InnterHTML is the only way to preserve element tags during the regex matches.
|
|
// However, since the replaced text is a capture group, only text in the document itself can cause a replacement
|
|
let resultString = '';
|
|
let prevFinalPos = 0;
|
|
let match;
|
|
while ((match = this.regexp.exec(element.innerHTML)) !== null) {
|
|
for (let i = 1; i < match.length; i++) {
|
|
if (!match.indices)
|
|
continue;
|
|
const replacement = `<span class="dtc-hide-match">${match[i]}</span>`;
|
|
const startPos = match.indices[i][0];
|
|
const finalPos = match.indices[i][1];
|
|
resultString += element.innerHTML.substring(prevFinalPos, startPos).concat(replacement);
|
|
prevFinalPos = finalPos;
|
|
}
|
|
}
|
|
if (resultString.length > 0) {
|
|
element.innerHTML = resultString;
|
|
}
|
|
};
|
|
// markdownPostProcessor manipulates the DOM of
|
|
// read mode to conceal clozure syntax
|
|
process = (htmlElement) => {
|
|
const elements = htmlElement.querySelectorAll(this.ELEMENTS_TO_PROCESS);
|
|
// Loop through each element
|
|
elements.forEach((element) => {
|
|
this.conceal(element);
|
|
});
|
|
};
|
|
}
|
|
|
|
// Settings
|
|
const DEFAULT_SETTINGS = {
|
|
doConcealEditMode: true,
|
|
regexp: ['({{1,2}(?![\\s{])(?:c?\\d+(?::{1,2}|\\|))?)(?:[^}]+)(}{1,2})'],
|
|
enable: true,
|
|
};
|
|
class DynamicTextConcealPlugin extends obsidian.Plugin {
|
|
settings;
|
|
editorExtensions = [];
|
|
async loadSettings() {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
|
}
|
|
async saveSettings() {
|
|
await this.saveData(this.settings);
|
|
}
|
|
addEditorExtension() {
|
|
this.editorExtensions.length = 0;
|
|
if (this.settings.doConcealEditMode) {
|
|
this.settings.regexp.forEach((regexString) => {
|
|
if (!regexString)
|
|
return; // skip if input is empty
|
|
// Create regex expression from user settings
|
|
// Note: the 'd' flag, enables regexpmatch indices for enabling clickable replacement text
|
|
// see: https://github.com/tc39/proposal-regexp-match-indices?tab=readme-ov-file#motivations
|
|
const regex = new RegExp(regexString, 'gmd');
|
|
this.editorExtensions.push(concealViewPlugin(regex));
|
|
});
|
|
}
|
|
}
|
|
updateEditorExtension() {
|
|
this.addEditorExtension();
|
|
this.app.workspace.updateOptions();
|
|
}
|
|
addEvents() {
|
|
if (this.settings.doConcealEditMode) {
|
|
// TODO: Add obsidian typing for EditorView to Editor
|
|
// See :
|
|
// - https://docs.obsidian.md/Plugins/Editor/Communicating+with+editor+extensions
|
|
// - https://github.com/blacksmithgu/obsidian-dataview/pull/2088/files
|
|
this.registerEvent(this.app.workspace.on('layout-change', () => {
|
|
this.app.workspace.iterateAllLeaves((leaf) => {
|
|
if (leaf.view instanceof obsidian.MarkdownView &&
|
|
// @ts-expect-error, not typed
|
|
leaf.view.editor.cm) {
|
|
// @ts-expect-error, not typed
|
|
const cm = leaf.view.editor.cm;
|
|
cm.dispatch({
|
|
effects: workspaceLayoutChangeEffect.of(null),
|
|
});
|
|
}
|
|
});
|
|
}));
|
|
}
|
|
}
|
|
addMarkdownPostProcessor() {
|
|
this.settings.regexp.forEach((regexString) => {
|
|
const regex = new RegExp(regexString, 'gmd'); // create regex expression from user settings
|
|
const concealPostProcessor = new ConcealPostProcessor(regex);
|
|
this.registerMarkdownPostProcessor(concealPostProcessor.process);
|
|
});
|
|
}
|
|
async onload() {
|
|
await this.loadSettings();
|
|
console.log('Loading Dynamic Text Conceal Plugin');
|
|
this.addMarkdownPostProcessor();
|
|
this.addEditorExtension();
|
|
this.registerEditorExtension(this.editorExtensions);
|
|
this.addEvents();
|
|
this.addSettingTab(new SettingsTab(this.app, this));
|
|
}
|
|
// Releases any resources configured by the plugin
|
|
onunload() {
|
|
console.log('Unloading Dynamic Text Conceal Plugin...');
|
|
}
|
|
}
|
|
|
|
module.exports = DynamicTextConcealPlugin;
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsiLi4vc3JjL3NldHRpbmdzVGFiLnRzIiwiLi4vc3JjL2VkaXRvckV4dGVuc2lvbnMvY29uY2VhbC1tYXRjaC1kZWNvcmF0b3IudHMiLCIuLi9zcmMvZWRpdG9yRXh0ZW5zaW9ucy9jb25jZWFsLXZpZXctcGx1Z2luLnRzIiwiLi4vc3JjL21hcmtkb3duUG9zdFByb2Nlc3NvcnMvY29uY2VhbC1wb3N0LXByb2Nlc3Nvci50cyIsIi4uL3NyYy9tYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpudWxsLCJuYW1lcyI6WyJQbHVnaW5TZXR0aW5nVGFiIiwiU2V0dGluZyIsIk1hdGNoRGVjb3JhdG9yIiwidmlldyIsIkRlY29yYXRpb24iLCJzeW50YXhUcmVlIiwiZWRpdG9yTGl2ZVByZXZpZXdGaWVsZCIsIlZpZXdQbHVnaW4iLCJTdGF0ZUVmZmVjdCIsIlBsdWdpbiIsIk1hcmtkb3duVmlldyJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztBQUdNLE1BQU8sV0FBWSxTQUFRQSx5QkFBZ0IsQ0FBQTtBQUNoRCxJQUFBLE1BQU0sQ0FBMkI7SUFFakMsV0FBWSxDQUFBLEdBQVEsRUFBRSxNQUFnQyxFQUFBO0FBQ3JELFFBQUEsS0FBSyxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUMsQ0FBQztBQUNuQixRQUFBLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0tBQ3JCO0FBRU8sSUFBQSxTQUFTLENBQUksR0FBUSxFQUFFLFNBQWlCLEVBQUUsT0FBZSxFQUFBO1FBQ2hFLElBQUksT0FBTyxHQUFHLENBQUMsSUFBSSxPQUFPLEtBQUssR0FBRyxDQUFDLE1BQU0sRUFBRTtZQUMxQyxPQUFPO1NBQ1A7QUFDRCxRQUFBLE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUMvQixHQUFHLENBQUMsU0FBUyxDQUFDLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQzlCLFFBQUEsR0FBRyxDQUFDLE9BQU8sQ0FBQyxH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
|