Oscar Plaisant 695358527b update
2024-09-25 16:35:23 +02:00

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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