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