3418 lines
		
	
	
		
			119 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			3418 lines
		
	
	
		
			119 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var obsidian = require('obsidian');
 | |
| var view = require('@codemirror/view');
 | |
| var language = require('@codemirror/language');
 | |
| var state = require('@codemirror/state');
 | |
| 
 | |
| /******************************************************************************
 | |
| Copyright (c) Microsoft Corporation.
 | |
| 
 | |
| Permission to use, copy, modify, and/or distribute this software for any
 | |
| purpose with or without fee is hereby granted.
 | |
| 
 | |
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 | |
| REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 | |
| AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 | |
| INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 | |
| LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 | |
| OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 | |
| PERFORMANCE OF THIS SOFTWARE.
 | |
| ***************************************************************************** */
 | |
| /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
 | |
| 
 | |
| 
 | |
| function __awaiter(thisArg, _arguments, P, generator) {
 | |
|     function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
 | |
|     return new (P || (P = Promise))(function (resolve, reject) {
 | |
|         function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
 | |
|         function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
 | |
|         function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
 | |
|         step((generator = generator.apply(thisArg, _arguments || [])).next());
 | |
|     });
 | |
| }
 | |
| 
 | |
| typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
 | |
|     var e = new Error(message);
 | |
|     return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
 | |
| };
 | |
| 
 | |
| class MoveCursorToPreviousUnfoldedLine {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         const list = this.root.getListUnderCursor();
 | |
|         const cursor = this.root.getCursor();
 | |
|         const lines = list.getLinesInfo();
 | |
|         const lineNo = lines.findIndex((l) => {
 | |
|             return (cursor.ch === l.from.ch + list.getCheckboxLength() &&
 | |
|                 cursor.line === l.from.line);
 | |
|         });
 | |
|         if (lineNo === 0) {
 | |
|             this.moveCursorToPreviousUnfoldedItem(root, cursor);
 | |
|         }
 | |
|         else if (lineNo > 0) {
 | |
|             this.moveCursorToPreviousNoteLine(root, lines, lineNo);
 | |
|         }
 | |
|     }
 | |
|     moveCursorToPreviousNoteLine(root, lines, lineNo) {
 | |
|         this.stopPropagation = true;
 | |
|         this.updated = true;
 | |
|         root.replaceCursor(lines[lineNo - 1].to);
 | |
|     }
 | |
|     moveCursorToPreviousUnfoldedItem(root, cursor) {
 | |
|         const prev = root.getListUnderLine(cursor.line - 1);
 | |
|         if (!prev) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         this.updated = true;
 | |
|         if (prev.isFolded()) {
 | |
|             const foldRoot = prev.getTopFoldRoot();
 | |
|             const firstLineEnd = foldRoot.getLinesInfo()[0].to;
 | |
|             root.replaceCursor(firstLineEnd);
 | |
|         }
 | |
|         else {
 | |
|             root.replaceCursor(prev.getLastLineContentEnd());
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| function getEditorFromState(state) {
 | |
|     const { editor } = state.field(obsidian.editorInfoField);
 | |
|     if (!editor) {
 | |
|         return null;
 | |
|     }
 | |
|     return new MyEditor(editor);
 | |
| }
 | |
| function foldInside(view, from, to) {
 | |
|     let found = null;
 | |
|     language.foldedRanges(view.state).between(from, to, (from, to) => {
 | |
|         if (!found || found.from > from)
 | |
|             found = { from, to };
 | |
|     });
 | |
|     return found;
 | |
| }
 | |
| class MyEditor {
 | |
|     constructor(e) {
 | |
|         this.e = e;
 | |
|         // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | |
|         this.view = this.e.cm;
 | |
|     }
 | |
|     getCursor() {
 | |
|         return this.e.getCursor();
 | |
|     }
 | |
|     getLine(n) {
 | |
|         return this.e.getLine(n);
 | |
|     }
 | |
|     lastLine() {
 | |
|         return this.e.lastLine();
 | |
|     }
 | |
|     listSelections() {
 | |
|         return this.e.listSelections();
 | |
|     }
 | |
|     getRange(from, to) {
 | |
|         return this.e.getRange(from, to);
 | |
|     }
 | |
|     replaceRange(replacement, from, to) {
 | |
|         return this.e.replaceRange(replacement, from, to);
 | |
|     }
 | |
|     setSelections(selections) {
 | |
|         this.e.setSelections(selections);
 | |
|     }
 | |
|     setValue(text) {
 | |
|         this.e.setValue(text);
 | |
|     }
 | |
|     getValue() {
 | |
|         return this.e.getValue();
 | |
|     }
 | |
|     offsetToPos(offset) {
 | |
|         return this.e.offsetToPos(offset);
 | |
|     }
 | |
|     posToOffset(pos) {
 | |
|         return this.e.posToOffset(pos);
 | |
|     }
 | |
|     fold(n) {
 | |
|         const { view } = this;
 | |
|         const l = view.lineBlockAt(view.state.doc.line(n + 1).from);
 | |
|         const range = language.foldable(view.state, l.from, l.to);
 | |
|         if (!range || range.from === range.to) {
 | |
|             return;
 | |
|         }
 | |
|         view.dispatch({ effects: [language.foldEffect.of(range)] });
 | |
|     }
 | |
|     unfold(n) {
 | |
|         const { view } = this;
 | |
|         const l = view.lineBlockAt(view.state.doc.line(n + 1).from);
 | |
|         const range = foldInside(view, l.from, l.to);
 | |
|         if (!range) {
 | |
|             return;
 | |
|         }
 | |
|         view.dispatch({ effects: [language.unfoldEffect.of(range)] });
 | |
|     }
 | |
|     getAllFoldedLines() {
 | |
|         const c = language.foldedRanges(this.view.state).iter();
 | |
|         const res = [];
 | |
|         while (c.value) {
 | |
|             res.push(this.offsetToPos(c.from).line);
 | |
|             c.next();
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
|     triggerOnKeyDown(e) {
 | |
|         view.runScopeHandlers(this.view, e, "editor");
 | |
|     }
 | |
|     getZoomRange() {
 | |
|         if (!window.ObsidianZoomPlugin) {
 | |
|             return null;
 | |
|         }
 | |
|         return window.ObsidianZoomPlugin.getZoomRange(this.e);
 | |
|     }
 | |
|     zoomOut() {
 | |
|         if (!window.ObsidianZoomPlugin) {
 | |
|             return;
 | |
|         }
 | |
|         window.ObsidianZoomPlugin.zoomOut(this.e);
 | |
|     }
 | |
|     zoomIn(line) {
 | |
|         if (!window.ObsidianZoomPlugin) {
 | |
|             return;
 | |
|         }
 | |
|         window.ObsidianZoomPlugin.zoomIn(this.e, line);
 | |
|     }
 | |
|     tryRefreshZoom(line) {
 | |
|         if (!window.ObsidianZoomPlugin) {
 | |
|             return;
 | |
|         }
 | |
|         if (window.ObsidianZoomPlugin.refreshZoom) {
 | |
|             window.ObsidianZoomPlugin.refreshZoom(this.e);
 | |
|         }
 | |
|         else {
 | |
|             window.ObsidianZoomPlugin.zoomIn(this.e, line);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| function createKeymapRunCallback(config) {
 | |
|     const check = config.check || (() => true);
 | |
|     const { run } = config;
 | |
|     return (view) => {
 | |
|         const editor = getEditorFromState(view.state);
 | |
|         if (!check(editor)) {
 | |
|             return false;
 | |
|         }
 | |
|         const { shouldUpdate, shouldStopPropagation } = run(editor);
 | |
|         return shouldUpdate || shouldStopPropagation;
 | |
|     };
 | |
| }
 | |
| 
 | |
| class ArrowLeftAndCtrlArrowLeftBehaviourOverride {
 | |
|     constructor(plugin, settings, imeDetector, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.imeDetector = imeDetector;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.check = () => {
 | |
|             return (this.settings.keepCursorWithinContent !== "never" &&
 | |
|                 !this.imeDetector.isOpened());
 | |
|         };
 | |
|         this.run = (editor) => {
 | |
|             return this.operationPerformer.perform((root) => new MoveCursorToPreviousUnfoldedLine(root), editor);
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(view.keymap.of([
 | |
|                 {
 | |
|                     key: "ArrowLeft",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|                 {
 | |
|                     win: "c-ArrowLeft",
 | |
|                     linux: "c-ArrowLeft",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|             ]));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| function cmpPos(a, b) {
 | |
|     return a.line - b.line || a.ch - b.ch;
 | |
| }
 | |
| function maxPos(a, b) {
 | |
|     return cmpPos(a, b) < 0 ? b : a;
 | |
| }
 | |
| function minPos(a, b) {
 | |
|     return cmpPos(a, b) < 0 ? a : b;
 | |
| }
 | |
| function isRangesIntersects(a, b) {
 | |
|     return cmpPos(a[1], b[0]) >= 0 && cmpPos(a[0], b[1]) <= 0;
 | |
| }
 | |
| function recalculateNumericBullets(root) {
 | |
|     function visit(parent) {
 | |
|         let index = 1;
 | |
|         for (const child of parent.getChildren()) {
 | |
|             if (/\d+\./.test(child.getBullet())) {
 | |
|                 child.replateBullet(`${index++}.`);
 | |
|             }
 | |
|             visit(child);
 | |
|         }
 | |
|     }
 | |
|     visit(root);
 | |
| }
 | |
| let idSeq = 0;
 | |
| class List {
 | |
|     constructor(root, indent, bullet, optionalCheckbox, spaceAfterBullet, firstLine, foldRoot) {
 | |
|         this.root = root;
 | |
|         this.indent = indent;
 | |
|         this.bullet = bullet;
 | |
|         this.optionalCheckbox = optionalCheckbox;
 | |
|         this.spaceAfterBullet = spaceAfterBullet;
 | |
|         this.foldRoot = foldRoot;
 | |
|         this.parent = null;
 | |
|         this.children = [];
 | |
|         this.notesIndent = null;
 | |
|         this.lines = [];
 | |
|         this.id = idSeq++;
 | |
|         this.lines.push(firstLine);
 | |
|     }
 | |
|     getID() {
 | |
|         return this.id;
 | |
|     }
 | |
|     getNotesIndent() {
 | |
|         return this.notesIndent;
 | |
|     }
 | |
|     setNotesIndent(notesIndent) {
 | |
|         if (this.notesIndent !== null) {
 | |
|             throw new Error(`Notes indent already provided`);
 | |
|         }
 | |
|         this.notesIndent = notesIndent;
 | |
|     }
 | |
|     addLine(text) {
 | |
|         if (this.notesIndent === null) {
 | |
|             throw new Error(`Unable to add line, notes indent should be provided first`);
 | |
|         }
 | |
|         this.lines.push(text);
 | |
|     }
 | |
|     replaceLines(lines) {
 | |
|         if (lines.length > 1 && this.notesIndent === null) {
 | |
|             throw new Error(`Unable to add line, notes indent should be provided first`);
 | |
|         }
 | |
|         this.lines = lines;
 | |
|     }
 | |
|     getLineCount() {
 | |
|         return this.lines.length;
 | |
|     }
 | |
|     getRoot() {
 | |
|         return this.root;
 | |
|     }
 | |
|     getChildren() {
 | |
|         return this.children.concat();
 | |
|     }
 | |
|     getLinesInfo() {
 | |
|         const startLine = this.root.getContentLinesRangeOf(this)[0];
 | |
|         return this.lines.map((row, i) => {
 | |
|             const line = startLine + i;
 | |
|             const startCh = i === 0 ? this.getContentStartCh() : this.notesIndent.length;
 | |
|             const endCh = startCh + row.length;
 | |
|             return {
 | |
|                 text: row,
 | |
|                 from: { line, ch: startCh },
 | |
|                 to: { line, ch: endCh },
 | |
|             };
 | |
|         });
 | |
|     }
 | |
|     getLines() {
 | |
|         return this.lines.concat();
 | |
|     }
 | |
|     getFirstLineContentStart() {
 | |
|         const startLine = this.root.getContentLinesRangeOf(this)[0];
 | |
|         return {
 | |
|             line: startLine,
 | |
|             ch: this.getContentStartCh(),
 | |
|         };
 | |
|     }
 | |
|     getFirstLineContentStartAfterCheckbox() {
 | |
|         const startLine = this.root.getContentLinesRangeOf(this)[0];
 | |
|         return {
 | |
|             line: startLine,
 | |
|             ch: this.getContentStartCh() + this.getCheckboxLength(),
 | |
|         };
 | |
|     }
 | |
|     getLastLineContentEnd() {
 | |
|         const endLine = this.root.getContentLinesRangeOf(this)[1];
 | |
|         const endCh = this.lines.length === 1
 | |
|             ? this.getContentStartCh() + this.lines[0].length
 | |
|             : this.notesIndent.length + this.lines[this.lines.length - 1].length;
 | |
|         return {
 | |
|             line: endLine,
 | |
|             ch: endCh,
 | |
|         };
 | |
|     }
 | |
|     getContentEndIncludingChildren() {
 | |
|         return this.getLastChild().getLastLineContentEnd();
 | |
|     }
 | |
|     getLastChild() {
 | |
|         let lastChild = this;
 | |
|         while (!lastChild.isEmpty()) {
 | |
|             lastChild = lastChild.getChildren().last();
 | |
|         }
 | |
|         return lastChild;
 | |
|     }
 | |
|     getContentStartCh() {
 | |
|         return this.indent.length + this.bullet.length + 1;
 | |
|     }
 | |
|     isFolded() {
 | |
|         if (this.foldRoot) {
 | |
|             return true;
 | |
|         }
 | |
|         if (this.parent) {
 | |
|             return this.parent.isFolded();
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
|     isFoldRoot() {
 | |
|         return this.foldRoot;
 | |
|     }
 | |
|     getTopFoldRoot() {
 | |
|         let tmp = this;
 | |
|         let foldRoot = null;
 | |
|         while (tmp) {
 | |
|             if (tmp.isFoldRoot()) {
 | |
|                 foldRoot = tmp;
 | |
|             }
 | |
|             tmp = tmp.parent;
 | |
|         }
 | |
|         return foldRoot;
 | |
|     }
 | |
|     getLevel() {
 | |
|         if (!this.parent) {
 | |
|             return 0;
 | |
|         }
 | |
|         return this.parent.getLevel() + 1;
 | |
|     }
 | |
|     unindentContent(from, till) {
 | |
|         this.indent = this.indent.slice(0, from) + this.indent.slice(till);
 | |
|         if (this.notesIndent !== null) {
 | |
|             this.notesIndent =
 | |
|                 this.notesIndent.slice(0, from) + this.notesIndent.slice(till);
 | |
|         }
 | |
|         for (const child of this.children) {
 | |
|             child.unindentContent(from, till);
 | |
|         }
 | |
|     }
 | |
|     indentContent(indentPos, indentChars) {
 | |
|         this.indent =
 | |
|             this.indent.slice(0, indentPos) +
 | |
|                 indentChars +
 | |
|                 this.indent.slice(indentPos);
 | |
|         if (this.notesIndent !== null) {
 | |
|             this.notesIndent =
 | |
|                 this.notesIndent.slice(0, indentPos) +
 | |
|                     indentChars +
 | |
|                     this.notesIndent.slice(indentPos);
 | |
|         }
 | |
|         for (const child of this.children) {
 | |
|             child.indentContent(indentPos, indentChars);
 | |
|         }
 | |
|     }
 | |
|     getFirstLineIndent() {
 | |
|         return this.indent;
 | |
|     }
 | |
|     getBullet() {
 | |
|         return this.bullet;
 | |
|     }
 | |
|     getSpaceAfterBullet() {
 | |
|         return this.spaceAfterBullet;
 | |
|     }
 | |
|     getCheckboxLength() {
 | |
|         return this.optionalCheckbox.length;
 | |
|     }
 | |
|     replateBullet(bullet) {
 | |
|         this.bullet = bullet;
 | |
|     }
 | |
|     getParent() {
 | |
|         return this.parent;
 | |
|     }
 | |
|     addBeforeAll(list) {
 | |
|         this.children.unshift(list);
 | |
|         list.parent = this;
 | |
|     }
 | |
|     addAfterAll(list) {
 | |
|         this.children.push(list);
 | |
|         list.parent = this;
 | |
|     }
 | |
|     removeChild(list) {
 | |
|         const i = this.children.indexOf(list);
 | |
|         this.children.splice(i, 1);
 | |
|         list.parent = null;
 | |
|     }
 | |
|     addBefore(before, list) {
 | |
|         const i = this.children.indexOf(before);
 | |
|         this.children.splice(i, 0, list);
 | |
|         list.parent = this;
 | |
|     }
 | |
|     addAfter(before, list) {
 | |
|         const i = this.children.indexOf(before);
 | |
|         this.children.splice(i + 1, 0, list);
 | |
|         list.parent = this;
 | |
|     }
 | |
|     getPrevSiblingOf(list) {
 | |
|         const i = this.children.indexOf(list);
 | |
|         return i > 0 ? this.children[i - 1] : null;
 | |
|     }
 | |
|     getNextSiblingOf(list) {
 | |
|         const i = this.children.indexOf(list);
 | |
|         return i >= 0 && i < this.children.length ? this.children[i + 1] : null;
 | |
|     }
 | |
|     isEmpty() {
 | |
|         return this.children.length === 0;
 | |
|     }
 | |
|     print() {
 | |
|         let res = "";
 | |
|         for (let i = 0; i < this.lines.length; i++) {
 | |
|             res +=
 | |
|                 i === 0
 | |
|                     ? this.indent + this.bullet + this.spaceAfterBullet
 | |
|                     : this.notesIndent;
 | |
|             res += this.lines[i];
 | |
|             res += "\n";
 | |
|         }
 | |
|         for (const child of this.children) {
 | |
|             res += child.print();
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
|     clone(newRoot) {
 | |
|         const clone = new List(newRoot, this.indent, this.bullet, this.optionalCheckbox, this.spaceAfterBullet, "", this.foldRoot);
 | |
|         clone.id = this.id;
 | |
|         clone.lines = this.lines.concat();
 | |
|         clone.notesIndent = this.notesIndent;
 | |
|         for (const child of this.children) {
 | |
|             clone.addAfterAll(child.clone(newRoot));
 | |
|         }
 | |
|         return clone;
 | |
|     }
 | |
| }
 | |
| class Root {
 | |
|     constructor(start, end, selections) {
 | |
|         this.start = start;
 | |
|         this.end = end;
 | |
|         this.rootList = new List(this, "", "", "", "", "", false);
 | |
|         this.selections = [];
 | |
|         this.replaceSelections(selections);
 | |
|     }
 | |
|     getRootList() {
 | |
|         return this.rootList;
 | |
|     }
 | |
|     getContentRange() {
 | |
|         return [this.getContentStart(), this.getContentEnd()];
 | |
|     }
 | |
|     getContentStart() {
 | |
|         return Object.assign({}, this.start);
 | |
|     }
 | |
|     getContentEnd() {
 | |
|         return Object.assign({}, this.end);
 | |
|     }
 | |
|     getSelections() {
 | |
|         return this.selections.map((s) => ({
 | |
|             anchor: Object.assign({}, s.anchor),
 | |
|             head: Object.assign({}, s.head),
 | |
|         }));
 | |
|     }
 | |
|     hasSingleCursor() {
 | |
|         if (!this.hasSingleSelection()) {
 | |
|             return false;
 | |
|         }
 | |
|         const selection = this.selections[0];
 | |
|         return (selection.anchor.line === selection.head.line &&
 | |
|             selection.anchor.ch === selection.head.ch);
 | |
|     }
 | |
|     hasSingleSelection() {
 | |
|         return this.selections.length === 1;
 | |
|     }
 | |
|     getSelection() {
 | |
|         const selection = this.selections[this.selections.length - 1];
 | |
|         const from = selection.anchor.ch > selection.head.ch
 | |
|             ? selection.head.ch
 | |
|             : selection.anchor.ch;
 | |
|         const to = selection.anchor.ch > selection.head.ch
 | |
|             ? selection.anchor.ch
 | |
|             : selection.head.ch;
 | |
|         return Object.assign(Object.assign({}, selection), { from,
 | |
|             to });
 | |
|     }
 | |
|     getCursor() {
 | |
|         return Object.assign({}, this.selections[this.selections.length - 1].head);
 | |
|     }
 | |
|     replaceCursor(cursor) {
 | |
|         this.selections = [{ anchor: cursor, head: cursor }];
 | |
|     }
 | |
|     replaceSelections(selections) {
 | |
|         if (selections.length < 1) {
 | |
|             throw new Error(`Unable to create Root without selections`);
 | |
|         }
 | |
|         this.selections = selections;
 | |
|     }
 | |
|     getListUnderCursor() {
 | |
|         return this.getListUnderLine(this.getCursor().line);
 | |
|     }
 | |
|     getListUnderLine(line) {
 | |
|         if (line < this.start.line || line > this.end.line) {
 | |
|             return;
 | |
|         }
 | |
|         let result = null;
 | |
|         let index = this.start.line;
 | |
|         const visitArr = (ll) => {
 | |
|             for (const l of ll) {
 | |
|                 const listFromLine = index;
 | |
|                 const listTillLine = listFromLine + l.getLineCount() - 1;
 | |
|                 if (line >= listFromLine && line <= listTillLine) {
 | |
|                     result = l;
 | |
|                 }
 | |
|                 else {
 | |
|                     index = listTillLine + 1;
 | |
|                     visitArr(l.getChildren());
 | |
|                 }
 | |
|                 if (result !== null) {
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
|         visitArr(this.rootList.getChildren());
 | |
|         return result;
 | |
|     }
 | |
|     getContentLinesRangeOf(list) {
 | |
|         let result = null;
 | |
|         let line = this.start.line;
 | |
|         const visitArr = (ll) => {
 | |
|             for (const l of ll) {
 | |
|                 const listFromLine = line;
 | |
|                 const listTillLine = listFromLine + l.getLineCount() - 1;
 | |
|                 if (l === list) {
 | |
|                     result = [listFromLine, listTillLine];
 | |
|                 }
 | |
|                 else {
 | |
|                     line = listTillLine + 1;
 | |
|                     visitArr(l.getChildren());
 | |
|                 }
 | |
|                 if (result !== null) {
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
|         visitArr(this.rootList.getChildren());
 | |
|         return result;
 | |
|     }
 | |
|     getChildren() {
 | |
|         return this.rootList.getChildren();
 | |
|     }
 | |
|     print() {
 | |
|         let res = "";
 | |
|         for (const child of this.rootList.getChildren()) {
 | |
|             res += child.print();
 | |
|         }
 | |
|         return res.replace(/\n$/, "");
 | |
|     }
 | |
|     clone() {
 | |
|         const clone = new Root(Object.assign({}, this.start), Object.assign({}, this.end), this.getSelections());
 | |
|         clone.rootList = this.rootList.clone(clone);
 | |
|         return clone;
 | |
|     }
 | |
| }
 | |
| 
 | |
| class DeleteTillPreviousLineContentEnd {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         const list = root.getListUnderCursor();
 | |
|         const cursor = root.getCursor();
 | |
|         const lines = list.getLinesInfo();
 | |
|         const lineNo = lines.findIndex((l) => cursor.ch === l.from.ch && cursor.line === l.from.line);
 | |
|         if (lineNo === 0) {
 | |
|             this.mergeWithPreviousItem(root, cursor, list);
 | |
|         }
 | |
|         else if (lineNo > 0) {
 | |
|             this.mergeNotes(root, cursor, list, lines, lineNo);
 | |
|         }
 | |
|     }
 | |
|     mergeNotes(root, cursor, list, lines, lineNo) {
 | |
|         this.stopPropagation = true;
 | |
|         this.updated = true;
 | |
|         const prevLineNo = lineNo - 1;
 | |
|         root.replaceCursor({
 | |
|             line: cursor.line - 1,
 | |
|             ch: lines[prevLineNo].text.length + lines[prevLineNo].from.ch,
 | |
|         });
 | |
|         lines[prevLineNo].text += lines[lineNo].text;
 | |
|         lines.splice(lineNo, 1);
 | |
|         list.replaceLines(lines.map((l) => l.text));
 | |
|     }
 | |
|     mergeWithPreviousItem(root, cursor, list) {
 | |
|         if (root.getChildren()[0] === list && list.isEmpty()) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         const prev = root.getListUnderLine(cursor.line - 1);
 | |
|         if (!prev) {
 | |
|             return;
 | |
|         }
 | |
|         const bothAreEmpty = prev.isEmpty() && list.isEmpty();
 | |
|         const prevIsEmptyAndSameLevel = prev.isEmpty() && !list.isEmpty() && prev.getLevel() === list.getLevel();
 | |
|         const listIsEmptyAndPrevIsParent = list.isEmpty() && prev.getLevel() === list.getLevel() - 1;
 | |
|         if (bothAreEmpty || prevIsEmptyAndSameLevel || listIsEmptyAndPrevIsParent) {
 | |
|             this.updated = true;
 | |
|             const parent = list.getParent();
 | |
|             const prevEnd = prev.getLastLineContentEnd();
 | |
|             if (!prev.getNotesIndent() && list.getNotesIndent()) {
 | |
|                 prev.setNotesIndent(prev.getFirstLineIndent() +
 | |
|                     list.getNotesIndent().slice(list.getFirstLineIndent().length));
 | |
|             }
 | |
|             const oldLines = prev.getLines();
 | |
|             const newLines = list.getLines();
 | |
|             oldLines[oldLines.length - 1] += newLines[0];
 | |
|             const resultLines = oldLines.concat(newLines.slice(1));
 | |
|             prev.replaceLines(resultLines);
 | |
|             parent.removeChild(list);
 | |
|             for (const c of list.getChildren()) {
 | |
|                 list.removeChild(c);
 | |
|                 prev.addAfterAll(c);
 | |
|             }
 | |
|             root.replaceCursor(prevEnd);
 | |
|             recalculateNumericBullets(root);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| class BackspaceBehaviourOverride {
 | |
|     constructor(plugin, settings, imeDetector, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.imeDetector = imeDetector;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.check = () => {
 | |
|             return (this.settings.keepCursorWithinContent !== "never" &&
 | |
|                 !this.imeDetector.isOpened());
 | |
|         };
 | |
|         this.run = (editor) => {
 | |
|             return this.operationPerformer.perform((root) => new DeleteTillPreviousLineContentEnd(root), editor);
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(view.keymap.of([
 | |
|                 {
 | |
|                     key: "Backspace",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|             ]));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| const BETTER_LISTS_BODY_CLASS = "outliner-plugin-better-lists";
 | |
| class BetterListsStyles {
 | |
|     constructor(settings, obsidianSettings) {
 | |
|         this.settings = settings;
 | |
|         this.obsidianSettings = obsidianSettings;
 | |
|         this.updateBodyClass = () => {
 | |
|             const shouldExists = this.obsidianSettings.isDefaultThemeEnabled() &&
 | |
|                 this.settings.betterListsStyles;
 | |
|             const exists = document.body.classList.contains(BETTER_LISTS_BODY_CLASS);
 | |
|             if (shouldExists && !exists) {
 | |
|                 document.body.classList.add(BETTER_LISTS_BODY_CLASS);
 | |
|             }
 | |
|             if (!shouldExists && exists) {
 | |
|                 document.body.classList.remove(BETTER_LISTS_BODY_CLASS);
 | |
|             }
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.updateBodyClass();
 | |
|             this.updateBodyClassInterval = window.setInterval(() => {
 | |
|                 this.updateBodyClass();
 | |
|             }, 1000);
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             clearInterval(this.updateBodyClassInterval);
 | |
|             document.body.classList.remove(BETTER_LISTS_BODY_CLASS);
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class SelectAllContent {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleSelection()) {
 | |
|             return;
 | |
|         }
 | |
|         const selection = root.getSelections()[0];
 | |
|         const [rootStart, rootEnd] = root.getContentRange();
 | |
|         const selectionFrom = minPos(selection.anchor, selection.head);
 | |
|         const selectionTo = maxPos(selection.anchor, selection.head);
 | |
|         if (selectionFrom.line < rootStart.line ||
 | |
|             selectionTo.line > rootEnd.line) {
 | |
|             return false;
 | |
|         }
 | |
|         if (selectionFrom.line === rootStart.line &&
 | |
|             selectionFrom.ch === rootStart.ch &&
 | |
|             selectionTo.line === rootEnd.line &&
 | |
|             selectionTo.ch === rootEnd.ch) {
 | |
|             return false;
 | |
|         }
 | |
|         const list = root.getListUnderCursor();
 | |
|         const contentStart = list.getFirstLineContentStartAfterCheckbox();
 | |
|         const contentEnd = list.getLastLineContentEnd();
 | |
|         const listUnderSelectionFrom = root.getListUnderLine(selectionFrom.line);
 | |
|         const listStart = listUnderSelectionFrom.getFirstLineContentStartAfterCheckbox();
 | |
|         const listEnd = listUnderSelectionFrom.getContentEndIncludingChildren();
 | |
|         this.stopPropagation = true;
 | |
|         this.updated = true;
 | |
|         if (selectionFrom.line === contentStart.line &&
 | |
|             selectionFrom.ch === contentStart.ch &&
 | |
|             selectionTo.line === contentEnd.line &&
 | |
|             selectionTo.ch === contentEnd.ch) {
 | |
|             if (list.getChildren().length) {
 | |
|                 // select sub lists
 | |
|                 root.replaceSelections([
 | |
|                     { anchor: contentStart, head: list.getContentEndIncludingChildren() },
 | |
|                 ]);
 | |
|             }
 | |
|             else {
 | |
|                 // select whole list
 | |
|                 root.replaceSelections([{ anchor: rootStart, head: rootEnd }]);
 | |
|             }
 | |
|         }
 | |
|         else if (listStart.ch == selectionFrom.ch &&
 | |
|             listEnd.line == selectionTo.line &&
 | |
|             listEnd.ch == selectionTo.ch) {
 | |
|             // select whole list
 | |
|             root.replaceSelections([{ anchor: rootStart, head: rootEnd }]);
 | |
|         }
 | |
|         else if ((selectionFrom.line > contentStart.line ||
 | |
|             (selectionFrom.line == contentStart.line &&
 | |
|                 selectionFrom.ch >= contentStart.ch)) &&
 | |
|             (selectionTo.line < contentEnd.line ||
 | |
|                 (selectionTo.line == contentEnd.line &&
 | |
|                     selectionTo.ch <= contentEnd.ch))) {
 | |
|             // select whole line
 | |
|             root.replaceSelections([{ anchor: contentStart, head: contentEnd }]);
 | |
|         }
 | |
|         else {
 | |
|             this.stopPropagation = false;
 | |
|             this.updated = false;
 | |
|             return false;
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| }
 | |
| 
 | |
| class CtrlAAndCmdABehaviourOverride {
 | |
|     constructor(plugin, settings, imeDetector, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.imeDetector = imeDetector;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.check = () => {
 | |
|             return (this.settings.overrideSelectAllBehaviour && !this.imeDetector.isOpened());
 | |
|         };
 | |
|         this.run = (editor) => {
 | |
|             return this.operationPerformer.perform((root) => new SelectAllContent(root), editor);
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(view.keymap.of([
 | |
|                 {
 | |
|                     key: "c-a",
 | |
|                     mac: "m-a",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|             ]));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class DeleteTillNextLineContentStart {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.deleteTillPreviousLineContentEnd =
 | |
|             new DeleteTillPreviousLineContentEnd(root);
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.deleteTillPreviousLineContentEnd.shouldStopPropagation();
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.deleteTillPreviousLineContentEnd.shouldUpdate();
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         const list = root.getListUnderCursor();
 | |
|         const cursor = root.getCursor();
 | |
|         const lines = list.getLinesInfo();
 | |
|         const lineNo = lines.findIndex((l) => cursor.ch === l.to.ch && cursor.line === l.to.line);
 | |
|         if (lineNo === lines.length - 1) {
 | |
|             const nextLine = lines[lineNo].to.line + 1;
 | |
|             const nextList = root.getListUnderLine(nextLine);
 | |
|             if (!nextList) {
 | |
|                 return;
 | |
|             }
 | |
|             root.replaceCursor(nextList.getFirstLineContentStart());
 | |
|             this.deleteTillPreviousLineContentEnd.perform();
 | |
|         }
 | |
|         else if (lineNo >= 0) {
 | |
|             root.replaceCursor(lines[lineNo + 1].from);
 | |
|             this.deleteTillPreviousLineContentEnd.perform();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| class DeleteBehaviourOverride {
 | |
|     constructor(plugin, settings, imeDetector, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.imeDetector = imeDetector;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.check = () => {
 | |
|             return (this.settings.keepCursorWithinContent !== "never" &&
 | |
|                 !this.imeDetector.isOpened());
 | |
|         };
 | |
|         this.run = (editor) => {
 | |
|             return this.operationPerformer.perform((root) => new DeleteTillNextLineContentStart(root), editor);
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(view.keymap.of([
 | |
|                 {
 | |
|                     key: "Delete",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|             ]));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class MoveListToDifferentPosition {
 | |
|     constructor(root, listToMove, placeToMove, whereToMove, defaultIndentChars) {
 | |
|         this.root = root;
 | |
|         this.listToMove = listToMove;
 | |
|         this.placeToMove = placeToMove;
 | |
|         this.whereToMove = whereToMove;
 | |
|         this.defaultIndentChars = defaultIndentChars;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         if (this.listToMove === this.placeToMove) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         this.updated = true;
 | |
|         const cursorAnchor = this.calculateCursorAnchor();
 | |
|         this.moveList();
 | |
|         this.changeIndent();
 | |
|         this.restoreCursor(cursorAnchor);
 | |
|         recalculateNumericBullets(this.root);
 | |
|     }
 | |
|     calculateCursorAnchor() {
 | |
|         const cursorLine = this.root.getCursor().line;
 | |
|         const lines = [
 | |
|             this.listToMove.getFirstLineContentStart().line,
 | |
|             this.listToMove.getLastLineContentEnd().line,
 | |
|             this.placeToMove.getFirstLineContentStart().line,
 | |
|             this.placeToMove.getLastLineContentEnd().line,
 | |
|         ];
 | |
|         const listStartLine = Math.min(...lines);
 | |
|         const listEndLine = Math.max(...lines);
 | |
|         if (cursorLine < listStartLine || cursorLine > listEndLine) {
 | |
|             return null;
 | |
|         }
 | |
|         const cursor = this.root.getCursor();
 | |
|         const cursorList = this.root.getListUnderLine(cursor.line);
 | |
|         const cursorListStart = cursorList.getFirstLineContentStart();
 | |
|         const lineDiff = cursor.line - cursorListStart.line;
 | |
|         const chDiff = cursor.ch - cursorListStart.ch;
 | |
|         return { cursorList, lineDiff, chDiff };
 | |
|     }
 | |
|     moveList() {
 | |
|         this.listToMove.getParent().removeChild(this.listToMove);
 | |
|         switch (this.whereToMove) {
 | |
|             case "before":
 | |
|                 this.placeToMove
 | |
|                     .getParent()
 | |
|                     .addBefore(this.placeToMove, this.listToMove);
 | |
|                 break;
 | |
|             case "after":
 | |
|                 this.placeToMove
 | |
|                     .getParent()
 | |
|                     .addAfter(this.placeToMove, this.listToMove);
 | |
|                 break;
 | |
|             case "inside":
 | |
|                 this.placeToMove.addBeforeAll(this.listToMove);
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
|     changeIndent() {
 | |
|         const oldIndent = this.listToMove.getFirstLineIndent();
 | |
|         const newIndent = this.whereToMove === "inside"
 | |
|             ? this.placeToMove.getFirstLineIndent() + this.defaultIndentChars
 | |
|             : this.placeToMove.getFirstLineIndent();
 | |
|         this.listToMove.unindentContent(0, oldIndent.length);
 | |
|         this.listToMove.indentContent(0, newIndent);
 | |
|     }
 | |
|     restoreCursor(cursorAnchor) {
 | |
|         if (cursorAnchor) {
 | |
|             const cursorListStart = cursorAnchor.cursorList.getFirstLineContentStart();
 | |
|             this.root.replaceCursor({
 | |
|                 line: cursorListStart.line + cursorAnchor.lineDiff,
 | |
|                 ch: cursorListStart.ch + cursorAnchor.chDiff,
 | |
|             });
 | |
|         }
 | |
|         else {
 | |
|             // When you move a list, the screen scrolls to the cursor.
 | |
|             // It is better to move the cursor into the viewport than let the screen scroll.
 | |
|             this.root.replaceCursor(this.listToMove.getLastLineContentEnd());
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| const BODY_CLASS = "outliner-plugin-dnd";
 | |
| class DragAndDrop {
 | |
|     constructor(plugin, settings, obisidian, parser, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.obisidian = obisidian;
 | |
|         this.parser = parser;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.preStart = null;
 | |
|         this.state = null;
 | |
|         this.handleSettingsChange = () => {
 | |
|             if (!isFeatureSupported()) {
 | |
|                 return;
 | |
|             }
 | |
|             if (this.settings.dragAndDrop) {
 | |
|                 document.body.classList.add(BODY_CLASS);
 | |
|             }
 | |
|             else {
 | |
|                 document.body.classList.remove(BODY_CLASS);
 | |
|             }
 | |
|         };
 | |
|         this.handleMouseDown = (e) => {
 | |
|             if (!isFeatureSupported() ||
 | |
|                 !this.settings.dragAndDrop ||
 | |
|                 !isClickOnBullet(e)) {
 | |
|                 return;
 | |
|             }
 | |
|             const view = getEditorViewFromHTMLElement(e.target);
 | |
|             if (!view) {
 | |
|                 return;
 | |
|             }
 | |
|             e.preventDefault();
 | |
|             e.stopPropagation();
 | |
|             this.preStart = {
 | |
|                 x: e.x,
 | |
|                 y: e.y,
 | |
|                 view,
 | |
|             };
 | |
|         };
 | |
|         this.handleMouseMove = (e) => {
 | |
|             if (this.preStart) {
 | |
|                 this.startDragging();
 | |
|             }
 | |
|             if (this.state) {
 | |
|                 this.detectAndDrawDropZone(e.x, e.y);
 | |
|             }
 | |
|         };
 | |
|         this.handleMouseUp = () => {
 | |
|             if (this.preStart) {
 | |
|                 this.preStart = null;
 | |
|             }
 | |
|             if (this.state) {
 | |
|                 this.stopDragging();
 | |
|             }
 | |
|         };
 | |
|         this.handleKeyDown = (e) => {
 | |
|             if (this.state && e.code === "Escape") {
 | |
|                 this.cancelDragging();
 | |
|             }
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension([
 | |
|                 draggingLinesStateField,
 | |
|                 droppingLinesStateField,
 | |
|             ]);
 | |
|             this.enableFeatureToggle();
 | |
|             this.createDropZone();
 | |
|             this.addEventListeners();
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.removeEventListeners();
 | |
|             this.removeDropZone();
 | |
|             this.disableFeatureToggle();
 | |
|         });
 | |
|     }
 | |
|     enableFeatureToggle() {
 | |
|         this.settings.onChange(this.handleSettingsChange);
 | |
|         this.handleSettingsChange();
 | |
|     }
 | |
|     disableFeatureToggle() {
 | |
|         this.settings.removeCallback(this.handleSettingsChange);
 | |
|         document.body.classList.remove(BODY_CLASS);
 | |
|     }
 | |
|     createDropZone() {
 | |
|         this.dropZonePadding = document.createElement("div");
 | |
|         this.dropZonePadding.classList.add("outliner-plugin-drop-zone-padding");
 | |
|         this.dropZone = document.createElement("div");
 | |
|         this.dropZone.classList.add("outliner-plugin-drop-zone");
 | |
|         this.dropZone.style.display = "none";
 | |
|         this.dropZone.appendChild(this.dropZonePadding);
 | |
|         document.body.appendChild(this.dropZone);
 | |
|     }
 | |
|     removeDropZone() {
 | |
|         document.body.removeChild(this.dropZone);
 | |
|         this.dropZonePadding = null;
 | |
|         this.dropZone = null;
 | |
|     }
 | |
|     addEventListeners() {
 | |
|         document.addEventListener("mousedown", this.handleMouseDown, {
 | |
|             capture: true,
 | |
|         });
 | |
|         document.addEventListener("mousemove", this.handleMouseMove);
 | |
|         document.addEventListener("mouseup", this.handleMouseUp);
 | |
|         document.addEventListener("keydown", this.handleKeyDown);
 | |
|     }
 | |
|     removeEventListeners() {
 | |
|         document.removeEventListener("mousedown", this.handleMouseDown, {
 | |
|             capture: true,
 | |
|         });
 | |
|         document.removeEventListener("mousemove", this.handleMouseMove);
 | |
|         document.removeEventListener("mouseup", this.handleMouseUp);
 | |
|         document.removeEventListener("keydown", this.handleKeyDown);
 | |
|     }
 | |
|     startDragging() {
 | |
|         const { x, y, view } = this.preStart;
 | |
|         this.preStart = null;
 | |
|         const editor = getEditorFromState(view.state);
 | |
|         const pos = editor.offsetToPos(view.posAtCoords({ x, y }));
 | |
|         const root = this.parser.parse(editor, pos);
 | |
|         const list = root.getListUnderLine(pos.line);
 | |
|         const state = new DragAndDropState(view, editor, root, list);
 | |
|         if (!state.hasDropVariants()) {
 | |
|             return;
 | |
|         }
 | |
|         this.state = state;
 | |
|         this.highlightDraggingLines();
 | |
|     }
 | |
|     detectAndDrawDropZone(x, y) {
 | |
|         this.state.calculateNearestDropVariant(x, y);
 | |
|         this.drawDropZone();
 | |
|     }
 | |
|     cancelDragging() {
 | |
|         this.state.dropVariant = null;
 | |
|         this.stopDragging();
 | |
|     }
 | |
|     stopDragging() {
 | |
|         this.unhightlightDraggingLines();
 | |
|         this.hideDropZone();
 | |
|         this.applyChanges();
 | |
|         this.state = null;
 | |
|     }
 | |
|     applyChanges() {
 | |
|         if (!this.state.dropVariant) {
 | |
|             return;
 | |
|         }
 | |
|         const { state } = this;
 | |
|         const { dropVariant, editor, root, list } = state;
 | |
|         const newRoot = this.parser.parse(editor, root.getContentStart());
 | |
|         if (!isSameRoots(root, newRoot)) {
 | |
|             new obsidian.Notice(`The item cannot be moved. The page content changed during the move.`, 5000);
 | |
|             return;
 | |
|         }
 | |
|         this.operationPerformer.eval(root, new MoveListToDifferentPosition(root, list, dropVariant.placeToMove, dropVariant.whereToMove, this.obisidian.getDefaultIndentChars()), editor);
 | |
|     }
 | |
|     highlightDraggingLines() {
 | |
|         const { state } = this;
 | |
|         const { list, editor, view } = state;
 | |
|         const lines = [];
 | |
|         const fromLine = list.getFirstLineContentStart().line;
 | |
|         const tillLine = list.getContentEndIncludingChildren().line;
 | |
|         for (let i = fromLine; i <= tillLine; i++) {
 | |
|             lines.push(editor.posToOffset({ line: i, ch: 0 }));
 | |
|         }
 | |
|         view.dispatch({
 | |
|             effects: [dndStarted.of(lines)],
 | |
|         });
 | |
|         document.body.classList.add("outliner-plugin-dragging");
 | |
|     }
 | |
|     unhightlightDraggingLines() {
 | |
|         document.body.classList.remove("outliner-plugin-dragging");
 | |
|         this.state.view.dispatch({
 | |
|             effects: [dndEnded.of()],
 | |
|         });
 | |
|     }
 | |
|     drawDropZone() {
 | |
|         const { state } = this;
 | |
|         const { view, editor, dropVariant } = state;
 | |
|         const newParent = dropVariant.whereToMove === "inside"
 | |
|             ? dropVariant.placeToMove
 | |
|             : dropVariant.placeToMove.getParent();
 | |
|         const newParentIsRootList = !newParent.getParent();
 | |
|         {
 | |
|             const width = Math.round(view.contentDOM.offsetWidth -
 | |
|                 (dropVariant.left - this.state.leftPadding));
 | |
|             this.dropZone.style.display = "block";
 | |
|             this.dropZone.style.top = dropVariant.top + "px";
 | |
|             this.dropZone.style.left = dropVariant.left + "px";
 | |
|             this.dropZone.style.width = width + "px";
 | |
|         }
 | |
|         {
 | |
|             const level = newParent.getLevel();
 | |
|             const indentWidth = this.state.tabWidth;
 | |
|             const width = indentWidth * level;
 | |
|             const dashPadding = 3;
 | |
|             const dashWidth = indentWidth - dashPadding;
 | |
|             const color = getComputedStyle(document.body).getPropertyValue("--color-accent");
 | |
|             this.dropZonePadding.style.width = `${width}px`;
 | |
|             this.dropZonePadding.style.marginLeft = `-${width}px`;
 | |
|             this.dropZonePadding.style.backgroundImage = `url('data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%20${width}%204%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cline%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%22${width}%22%20y2%3D%220%22%20stroke%3D%22${color}%22%20stroke-width%3D%228%22%20stroke-dasharray%3D%22${dashWidth}%20${dashPadding}%22%2F%3E%3C%2Fsvg%3E')`;
 | |
|         }
 | |
|         this.state.view.dispatch({
 | |
|             effects: [
 | |
|                 dndMoved.of(newParentIsRootList
 | |
|                     ? null
 | |
|                     : editor.posToOffset({
 | |
|                         line: newParent.getFirstLineContentStart().line,
 | |
|                         ch: 0,
 | |
|                     })),
 | |
|             ],
 | |
|         });
 | |
|     }
 | |
|     hideDropZone() {
 | |
|         this.dropZone.style.display = "none";
 | |
|     }
 | |
| }
 | |
| class DragAndDropState {
 | |
|     constructor(view, editor, root, list) {
 | |
|         this.view = view;
 | |
|         this.editor = editor;
 | |
|         this.root = root;
 | |
|         this.list = list;
 | |
|         this.dropVariants = new Map();
 | |
|         this.dropVariant = null;
 | |
|         this.leftPadding = 0;
 | |
|         this.tabWidth = 0;
 | |
|         this.collectDropVariants();
 | |
|         this.calculateLeftPadding();
 | |
|         this.calculateTabWidth();
 | |
|     }
 | |
|     getDropVariants() {
 | |
|         return Array.from(this.dropVariants.values());
 | |
|     }
 | |
|     hasDropVariants() {
 | |
|         return this.dropVariants.size > 0;
 | |
|     }
 | |
|     calculateNearestDropVariant(x, y) {
 | |
|         const { view, editor } = this;
 | |
|         const dropVariants = this.getDropVariants();
 | |
|         const possibleDropVariants = [];
 | |
|         for (const v of dropVariants) {
 | |
|             const { placeToMove } = v;
 | |
|             const positionAfterList = v.whereToMove === "after" || v.whereToMove === "inside";
 | |
|             const line = positionAfterList
 | |
|                 ? placeToMove.getContentEndIncludingChildren().line
 | |
|                 : placeToMove.getFirstLineContentStart().line;
 | |
|             const linePos = editor.posToOffset({
 | |
|                 line,
 | |
|                 ch: 0,
 | |
|             });
 | |
|             const coords = view.coordsAtPos(linePos, -1);
 | |
|             if (!coords) {
 | |
|                 continue;
 | |
|             }
 | |
|             v.left = this.leftPadding + (v.level - 1) * this.tabWidth;
 | |
|             v.top = coords.top;
 | |
|             if (positionAfterList) {
 | |
|                 v.top += view.lineBlockAt(linePos).height;
 | |
|             }
 | |
|             // Better vertical alignment
 | |
|             v.top -= 8;
 | |
|             possibleDropVariants.push(v);
 | |
|         }
 | |
|         const nearestLineTop = possibleDropVariants
 | |
|             .sort((a, b) => Math.abs(y - a.top) - Math.abs(y - b.top))
 | |
|             .first().top;
 | |
|         const variansOnNearestLine = possibleDropVariants.filter((v) => Math.abs(v.top - nearestLineTop) <= 4);
 | |
|         this.dropVariant = variansOnNearestLine
 | |
|             .sort((a, b) => Math.abs(x - a.left) - Math.abs(x - b.left))
 | |
|             .first();
 | |
|     }
 | |
|     addDropVariant(v) {
 | |
|         this.dropVariants.set(`${v.line} ${v.level}`, v);
 | |
|     }
 | |
|     collectDropVariants() {
 | |
|         const visit = (lists) => {
 | |
|             for (const placeToMove of lists) {
 | |
|                 const lineBefore = placeToMove.getFirstLineContentStart().line;
 | |
|                 const lineAfter = placeToMove.getContentEndIncludingChildren().line + 1;
 | |
|                 const level = placeToMove.getLevel();
 | |
|                 this.addDropVariant({
 | |
|                     line: lineBefore,
 | |
|                     level,
 | |
|                     left: 0,
 | |
|                     top: 0,
 | |
|                     placeToMove,
 | |
|                     whereToMove: "before",
 | |
|                 });
 | |
|                 this.addDropVariant({
 | |
|                     line: lineAfter,
 | |
|                     level,
 | |
|                     left: 0,
 | |
|                     top: 0,
 | |
|                     placeToMove,
 | |
|                     whereToMove: "after",
 | |
|                 });
 | |
|                 if (placeToMove === this.list) {
 | |
|                     continue;
 | |
|                 }
 | |
|                 if (placeToMove.isEmpty()) {
 | |
|                     this.addDropVariant({
 | |
|                         line: lineAfter,
 | |
|                         level: level + 1,
 | |
|                         left: 0,
 | |
|                         top: 0,
 | |
|                         placeToMove,
 | |
|                         whereToMove: "inside",
 | |
|                     });
 | |
|                 }
 | |
|                 else {
 | |
|                     visit(placeToMove.getChildren());
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
|         visit(this.root.getChildren());
 | |
|     }
 | |
|     calculateLeftPadding() {
 | |
|         const cmLine = this.view.dom.querySelector("div.cm-line");
 | |
|         this.leftPadding = cmLine.getBoundingClientRect().left;
 | |
|     }
 | |
|     calculateTabWidth() {
 | |
|         const { view } = this;
 | |
|         const indentDom = view.dom.querySelector(".cm-indent");
 | |
|         if (indentDom) {
 | |
|             this.tabWidth = indentDom.offsetWidth;
 | |
|             return;
 | |
|         }
 | |
|         const singleIndent = language.indentString(view.state, language.getIndentUnit(view.state));
 | |
|         for (let i = 1; i <= view.state.doc.lines; i++) {
 | |
|             const line = view.state.doc.line(i);
 | |
|             if (line.text.startsWith(singleIndent)) {
 | |
|                 const a = view.coordsAtPos(line.from, -1);
 | |
|                 if (!a) {
 | |
|                     continue;
 | |
|                 }
 | |
|                 const b = view.coordsAtPos(line.from + singleIndent.length, -1);
 | |
|                 if (!b) {
 | |
|                     continue;
 | |
|                 }
 | |
|                 this.tabWidth = b.left - a.left;
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         this.tabWidth = view.defaultCharacterWidth * language.getIndentUnit(view.state);
 | |
|     }
 | |
| }
 | |
| const dndStarted = state.StateEffect.define({
 | |
|     map: (lines, change) => lines.map((l) => change.mapPos(l)),
 | |
| });
 | |
| const dndMoved = state.StateEffect.define({
 | |
|     map: (line, change) => (line !== null ? change.mapPos(line) : line),
 | |
| });
 | |
| const dndEnded = state.StateEffect.define();
 | |
| const draggingLineDecoration = view.Decoration.line({
 | |
|     class: "outliner-plugin-dragging-line",
 | |
| });
 | |
| const droppingLineDecoration = view.Decoration.line({
 | |
|     class: "outliner-plugin-dropping-line",
 | |
| });
 | |
| const draggingLinesStateField = state.StateField.define({
 | |
|     create: () => view.Decoration.none,
 | |
|     update: (dndState, tr) => {
 | |
|         dndState = dndState.map(tr.changes);
 | |
|         for (const e of tr.effects) {
 | |
|             if (e.is(dndStarted)) {
 | |
|                 dndState = dndState.update({
 | |
|                     add: e.value.map((l) => draggingLineDecoration.range(l, l)),
 | |
|                 });
 | |
|             }
 | |
|             if (e.is(dndEnded)) {
 | |
|                 dndState = view.Decoration.none;
 | |
|             }
 | |
|         }
 | |
|         return dndState;
 | |
|     },
 | |
|     provide: (f) => view.EditorView.decorations.from(f),
 | |
| });
 | |
| const droppingLinesStateField = state.StateField.define({
 | |
|     create: () => view.Decoration.none,
 | |
|     update: (dndDroppingState, tr) => {
 | |
|         dndDroppingState = dndDroppingState.map(tr.changes);
 | |
|         for (const e of tr.effects) {
 | |
|             if (e.is(dndMoved)) {
 | |
|                 dndDroppingState =
 | |
|                     e.value === null
 | |
|                         ? view.Decoration.none
 | |
|                         : view.Decoration.set(droppingLineDecoration.range(e.value, e.value));
 | |
|             }
 | |
|             if (e.is(dndEnded)) {
 | |
|                 dndDroppingState = view.Decoration.none;
 | |
|             }
 | |
|         }
 | |
|         return dndDroppingState;
 | |
|     },
 | |
|     provide: (f) => view.EditorView.decorations.from(f),
 | |
| });
 | |
| function getEditorViewFromHTMLElement(e) {
 | |
|     while (e && !e.classList.contains("cm-editor")) {
 | |
|         e = e.parentElement;
 | |
|     }
 | |
|     if (!e) {
 | |
|         return null;
 | |
|     }
 | |
|     return view.EditorView.findFromDOM(e);
 | |
| }
 | |
| function isClickOnBullet(e) {
 | |
|     let el = e.target;
 | |
|     while (el) {
 | |
|         if (el.classList.contains("cm-formatting-list") ||
 | |
|             el.classList.contains("cm-fold-indicator") ||
 | |
|             el.classList.contains("task-list-item-checkbox")) {
 | |
|             return true;
 | |
|         }
 | |
|         el = el.parentElement;
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| function isSameRoots(a, b) {
 | |
|     const [aStart, aEnd] = a.getContentRange();
 | |
|     const [bStart, bEnd] = b.getContentRange();
 | |
|     if (cmpPos(aStart, bStart) !== 0 || cmpPos(aEnd, bEnd) !== 0) {
 | |
|         return false;
 | |
|     }
 | |
|     return a.print() === b.print();
 | |
| }
 | |
| function isFeatureSupported() {
 | |
|     return obsidian.Platform.isDesktop;
 | |
| }
 | |
| 
 | |
| class KeepCursorOutsideFoldedLines {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         const cursor = root.getCursor();
 | |
|         const list = root.getListUnderCursor();
 | |
|         if (!list.isFolded()) {
 | |
|             return;
 | |
|         }
 | |
|         const foldRoot = list.getTopFoldRoot();
 | |
|         const firstLineEnd = foldRoot.getLinesInfo()[0].to;
 | |
|         if (cursor.line > firstLineEnd.line) {
 | |
|             this.updated = true;
 | |
|             this.stopPropagation = true;
 | |
|             root.replaceCursor(firstLineEnd);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| class KeepCursorWithinListContent {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         const cursor = root.getCursor();
 | |
|         const list = root.getListUnderCursor();
 | |
|         const contentStart = list.getFirstLineContentStartAfterCheckbox();
 | |
|         const linePrefix = contentStart.line === cursor.line
 | |
|             ? contentStart.ch
 | |
|             : list.getNotesIndent().length;
 | |
|         if (cursor.ch < linePrefix) {
 | |
|             this.updated = true;
 | |
|             this.stopPropagation = true;
 | |
|             root.replaceCursor({
 | |
|                 line: cursor.line,
 | |
|                 ch: linePrefix,
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| class EditorSelectionsBehaviourOverride {
 | |
|     constructor(plugin, settings, parser, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.parser = parser;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.transactionExtender = (tr) => {
 | |
|             if (this.settings.keepCursorWithinContent === "never" || !tr.selection) {
 | |
|                 return null;
 | |
|             }
 | |
|             const editor = getEditorFromState(tr.startState);
 | |
|             setTimeout(() => {
 | |
|                 this.handleSelectionsChanges(editor);
 | |
|             }, 0);
 | |
|             return null;
 | |
|         };
 | |
|         this.handleSelectionsChanges = (editor) => {
 | |
|             const root = this.parser.parse(editor);
 | |
|             if (!root) {
 | |
|                 return;
 | |
|             }
 | |
|             {
 | |
|                 const { shouldStopPropagation } = this.operationPerformer.eval(root, new KeepCursorOutsideFoldedLines(root), editor);
 | |
|                 if (shouldStopPropagation) {
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|             this.operationPerformer.eval(root, new KeepCursorWithinListContent(root), editor);
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(state.EditorState.transactionExtender.of(this.transactionExtender));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| const checkboxRe = `\\[[^\\[\\]]\\][ \t]`;
 | |
| 
 | |
| function isEmptyLineOrEmptyCheckbox(line) {
 | |
|     return line === "" || line === "[ ] ";
 | |
| }
 | |
| 
 | |
| class CreateNewItem {
 | |
|     constructor(root, defaultIndentChars, getZoomRange, after = true) {
 | |
|         this.root = root;
 | |
|         this.defaultIndentChars = defaultIndentChars;
 | |
|         this.getZoomRange = getZoomRange;
 | |
|         this.after = after;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleSelection()) {
 | |
|             return;
 | |
|         }
 | |
|         const selection = root.getSelection();
 | |
|         if (!selection || selection.anchor.line !== selection.head.line) {
 | |
|             return;
 | |
|         }
 | |
|         const list = root.getListUnderCursor();
 | |
|         const lines = list.getLinesInfo();
 | |
|         if (lines.length === 1 && isEmptyLineOrEmptyCheckbox(lines[0].text)) {
 | |
|             return;
 | |
|         }
 | |
|         const cursor = root.getCursor();
 | |
|         const lineUnderCursor = lines.find((l) => l.from.line === cursor.line);
 | |
|         if (cursor.ch < lineUnderCursor.from.ch) {
 | |
|             return;
 | |
|         }
 | |
|         const { oldLines, newLines } = lines.reduce((acc, line) => {
 | |
|             if (cursor.line > line.from.line) {
 | |
|                 acc.oldLines.push(line.text);
 | |
|             }
 | |
|             else if (cursor.line === line.from.line) {
 | |
|                 const left = line.text.slice(0, selection.from - line.from.ch);
 | |
|                 const right = line.text.slice(selection.to - line.from.ch);
 | |
|                 acc.oldLines.push(left);
 | |
|                 acc.newLines.push(right);
 | |
|             }
 | |
|             else if (cursor.line < line.from.line) {
 | |
|                 acc.newLines.push(line.text);
 | |
|             }
 | |
|             return acc;
 | |
|         }, {
 | |
|             oldLines: [],
 | |
|             newLines: [],
 | |
|         });
 | |
|         const codeBlockBacticks = oldLines.join("\n").split("```").length - 1;
 | |
|         const isInsideCodeblock = codeBlockBacticks > 0 && codeBlockBacticks % 2 !== 0;
 | |
|         if (isInsideCodeblock) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         this.updated = true;
 | |
|         const zoomRange = this.getZoomRange.getZoomRange();
 | |
|         const listIsZoomingRoot = Boolean(zoomRange &&
 | |
|             list.getFirstLineContentStart().line >= zoomRange.from.line &&
 | |
|             list.getLastLineContentEnd().line <= zoomRange.from.line);
 | |
|         const hasChildren = !list.isEmpty();
 | |
|         const childIsFolded = list.isFoldRoot();
 | |
|         const endPos = list.getLastLineContentEnd();
 | |
|         const endOfLine = cursor.line === endPos.line && cursor.ch === endPos.ch;
 | |
|         const onChildLevel = listIsZoomingRoot || (hasChildren && !childIsFolded && endOfLine);
 | |
|         const indent = onChildLevel
 | |
|             ? hasChildren
 | |
|                 ? list.getChildren()[0].getFirstLineIndent()
 | |
|                 : list.getFirstLineIndent() + this.defaultIndentChars
 | |
|             : list.getFirstLineIndent();
 | |
|         const bullet = onChildLevel && hasChildren
 | |
|             ? list.getChildren()[0].getBullet()
 | |
|             : list.getBullet();
 | |
|         const spaceAfterBullet = onChildLevel && hasChildren
 | |
|             ? list.getChildren()[0].getSpaceAfterBullet()
 | |
|             : list.getSpaceAfterBullet();
 | |
|         const prefix = oldLines[0].match(checkboxRe) ? "[ ] " : "";
 | |
|         const newList = new List(list.getRoot(), indent, bullet, prefix, spaceAfterBullet, prefix + newLines.shift(), false);
 | |
|         if (newLines.length > 0) {
 | |
|             newList.setNotesIndent(list.getNotesIndent());
 | |
|             for (const line of newLines) {
 | |
|                 newList.addLine(line);
 | |
|             }
 | |
|         }
 | |
|         if (onChildLevel) {
 | |
|             list.addBeforeAll(newList);
 | |
|         }
 | |
|         else {
 | |
|             if (!childIsFolded || !endOfLine) {
 | |
|                 const children = list.getChildren();
 | |
|                 for (const child of children) {
 | |
|                     list.removeChild(child);
 | |
|                     newList.addAfterAll(child);
 | |
|                 }
 | |
|             }
 | |
|             if (this.after) {
 | |
|                 list.getParent().addAfter(list, newList);
 | |
|             }
 | |
|             else {
 | |
|                 list.getParent().addBefore(list, newList);
 | |
|             }
 | |
|         }
 | |
|         list.replaceLines(oldLines);
 | |
|         const newListStart = newList.getFirstLineContentStart();
 | |
|         root.replaceCursor({
 | |
|             line: newListStart.line,
 | |
|             ch: newListStart.ch + prefix.length,
 | |
|         });
 | |
|         recalculateNumericBullets(root);
 | |
|     }
 | |
| }
 | |
| 
 | |
| class OutdentList {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         const list = root.getListUnderCursor();
 | |
|         const parent = list.getParent();
 | |
|         const grandParent = parent.getParent();
 | |
|         if (!grandParent) {
 | |
|             return;
 | |
|         }
 | |
|         this.updated = true;
 | |
|         const listStartLineBefore = root.getContentLinesRangeOf(list)[0];
 | |
|         const indentRmFrom = parent.getFirstLineIndent().length;
 | |
|         const indentRmTill = list.getFirstLineIndent().length;
 | |
|         parent.removeChild(list);
 | |
|         grandParent.addAfter(parent, list);
 | |
|         list.unindentContent(indentRmFrom, indentRmTill);
 | |
|         const listStartLineAfter = root.getContentLinesRangeOf(list)[0];
 | |
|         const lineDiff = listStartLineAfter - listStartLineBefore;
 | |
|         const chDiff = indentRmTill - indentRmFrom;
 | |
|         const cursor = root.getCursor();
 | |
|         root.replaceCursor({
 | |
|             line: cursor.line + lineDiff,
 | |
|             ch: cursor.ch - chDiff,
 | |
|         });
 | |
|         recalculateNumericBullets(root);
 | |
|     }
 | |
| }
 | |
| 
 | |
| class OutdentListIfItsEmpty {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.outdentList = new OutdentList(root);
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.outdentList.shouldStopPropagation();
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.outdentList.shouldUpdate();
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         const list = root.getListUnderCursor();
 | |
|         const lines = list.getLines();
 | |
|         if (lines.length > 1 ||
 | |
|             !isEmptyLineOrEmptyCheckbox(lines[0]) ||
 | |
|             list.getLevel() === 1) {
 | |
|             return;
 | |
|         }
 | |
|         this.outdentList.perform();
 | |
|     }
 | |
| }
 | |
| 
 | |
| class EnterBehaviourOverride {
 | |
|     constructor(plugin, settings, imeDetector, obsidianSettings, parser, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.imeDetector = imeDetector;
 | |
|         this.obsidianSettings = obsidianSettings;
 | |
|         this.parser = parser;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.check = () => {
 | |
|             return this.settings.overrideEnterBehaviour && !this.imeDetector.isOpened();
 | |
|         };
 | |
|         this.run = (editor) => {
 | |
|             const root = this.parser.parse(editor);
 | |
|             if (!root) {
 | |
|                 return {
 | |
|                     shouldUpdate: false,
 | |
|                     shouldStopPropagation: false,
 | |
|                 };
 | |
|             }
 | |
|             {
 | |
|                 const res = this.operationPerformer.eval(root, new OutdentListIfItsEmpty(root), editor);
 | |
|                 if (res.shouldStopPropagation) {
 | |
|                     return res;
 | |
|                 }
 | |
|             }
 | |
|             {
 | |
|                 const defaultIndentChars = this.obsidianSettings.getDefaultIndentChars();
 | |
|                 const zoomRange = editor.getZoomRange();
 | |
|                 const getZoomRange = {
 | |
|                     getZoomRange: () => zoomRange,
 | |
|                 };
 | |
|                 const res = this.operationPerformer.eval(root, new CreateNewItem(root, defaultIndentChars, getZoomRange), editor);
 | |
|                 if (res.shouldUpdate && zoomRange) {
 | |
|                     editor.tryRefreshZoom(zoomRange.from.line);
 | |
|                 }
 | |
|                 return res;
 | |
|             }
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(state.Prec.highest(view.keymap.of([
 | |
|                 {
 | |
|                     key: "Enter",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|             ])));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| function createEditorCallback(cb) {
 | |
|     return (editor) => {
 | |
|         const myEditor = new MyEditor(editor);
 | |
|         const shouldStopPropagation = cb(myEditor);
 | |
|         if (!shouldStopPropagation &&
 | |
|             window.event &&
 | |
|             window.event.type === "keydown") {
 | |
|             myEditor.triggerOnKeyDown(window.event);
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| class ListsFoldingCommands {
 | |
|     constructor(plugin, obsidianSettings) {
 | |
|         this.plugin = plugin;
 | |
|         this.obsidianSettings = obsidianSettings;
 | |
|         this.fold = (editor) => {
 | |
|             return this.setFold(editor, "fold");
 | |
|         };
 | |
|         this.unfold = (editor) => {
 | |
|             return this.setFold(editor, "unfold");
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.addCommand({
 | |
|                 id: "fold",
 | |
|                 icon: "chevrons-down-up",
 | |
|                 name: "Fold the list",
 | |
|                 editorCallback: createEditorCallback(this.fold),
 | |
|                 hotkeys: [
 | |
|                     {
 | |
|                         modifiers: ["Mod"],
 | |
|                         key: "ArrowUp",
 | |
|                     },
 | |
|                 ],
 | |
|             });
 | |
|             this.plugin.addCommand({
 | |
|                 id: "unfold",
 | |
|                 icon: "chevrons-up-down",
 | |
|                 name: "Unfold the list",
 | |
|                 editorCallback: createEditorCallback(this.unfold),
 | |
|                 hotkeys: [
 | |
|                     {
 | |
|                         modifiers: ["Mod"],
 | |
|                         key: "ArrowDown",
 | |
|                     },
 | |
|                 ],
 | |
|             });
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
|     setFold(editor, type) {
 | |
|         if (!this.obsidianSettings.getFoldSettings().foldIndent) {
 | |
|             new obsidian.Notice(`Unable to ${type} because folding is disabled. Please enable "Fold indent" in Obsidian settings.`, 5000);
 | |
|             return true;
 | |
|         }
 | |
|         const cursor = editor.getCursor();
 | |
|         if (type === "fold") {
 | |
|             editor.fold(cursor.line);
 | |
|         }
 | |
|         else {
 | |
|             editor.unfold(cursor.line);
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| }
 | |
| 
 | |
| class IndentList {
 | |
|     constructor(root, defaultIndentChars) {
 | |
|         this.root = root;
 | |
|         this.defaultIndentChars = defaultIndentChars;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         const list = root.getListUnderCursor();
 | |
|         const parent = list.getParent();
 | |
|         const prev = parent.getPrevSiblingOf(list);
 | |
|         if (!prev) {
 | |
|             return;
 | |
|         }
 | |
|         this.updated = true;
 | |
|         const listStartLineBefore = root.getContentLinesRangeOf(list)[0];
 | |
|         const indentPos = list.getFirstLineIndent().length;
 | |
|         let indentChars = "";
 | |
|         if (indentChars === "" && !prev.isEmpty()) {
 | |
|             indentChars = prev
 | |
|                 .getChildren()[0]
 | |
|                 .getFirstLineIndent()
 | |
|                 .slice(prev.getFirstLineIndent().length);
 | |
|         }
 | |
|         if (indentChars === "") {
 | |
|             indentChars = list
 | |
|                 .getFirstLineIndent()
 | |
|                 .slice(parent.getFirstLineIndent().length);
 | |
|         }
 | |
|         if (indentChars === "" && !list.isEmpty()) {
 | |
|             indentChars = list.getChildren()[0].getFirstLineIndent();
 | |
|         }
 | |
|         if (indentChars === "") {
 | |
|             indentChars = this.defaultIndentChars;
 | |
|         }
 | |
|         parent.removeChild(list);
 | |
|         prev.addAfterAll(list);
 | |
|         list.indentContent(indentPos, indentChars);
 | |
|         const listStartLineAfter = root.getContentLinesRangeOf(list)[0];
 | |
|         const lineDiff = listStartLineAfter - listStartLineBefore;
 | |
|         const cursor = root.getCursor();
 | |
|         root.replaceCursor({
 | |
|             line: cursor.line + lineDiff,
 | |
|             ch: cursor.ch + indentChars.length,
 | |
|         });
 | |
|         recalculateNumericBullets(root);
 | |
|     }
 | |
| }
 | |
| 
 | |
| class MoveListDown {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         const list = root.getListUnderCursor();
 | |
|         const parent = list.getParent();
 | |
|         const grandParent = parent.getParent();
 | |
|         const next = parent.getNextSiblingOf(list);
 | |
|         const listStartLineBefore = root.getContentLinesRangeOf(list)[0];
 | |
|         if (!next && grandParent) {
 | |
|             const newParent = grandParent.getNextSiblingOf(parent);
 | |
|             if (newParent) {
 | |
|                 this.updated = true;
 | |
|                 parent.removeChild(list);
 | |
|                 newParent.addBeforeAll(list);
 | |
|             }
 | |
|         }
 | |
|         else if (next) {
 | |
|             this.updated = true;
 | |
|             parent.removeChild(list);
 | |
|             parent.addAfter(next, list);
 | |
|         }
 | |
|         if (!this.updated) {
 | |
|             return;
 | |
|         }
 | |
|         const listStartLineAfter = root.getContentLinesRangeOf(list)[0];
 | |
|         const lineDiff = listStartLineAfter - listStartLineBefore;
 | |
|         const cursor = root.getCursor();
 | |
|         root.replaceCursor({
 | |
|             line: cursor.line + lineDiff,
 | |
|             ch: cursor.ch,
 | |
|         });
 | |
|         recalculateNumericBullets(root);
 | |
|     }
 | |
| }
 | |
| 
 | |
| class MoveListUp {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         const list = root.getListUnderCursor();
 | |
|         const parent = list.getParent();
 | |
|         const grandParent = parent.getParent();
 | |
|         const prev = parent.getPrevSiblingOf(list);
 | |
|         const listStartLineBefore = root.getContentLinesRangeOf(list)[0];
 | |
|         if (!prev && grandParent) {
 | |
|             const newParent = grandParent.getPrevSiblingOf(parent);
 | |
|             if (newParent) {
 | |
|                 this.updated = true;
 | |
|                 parent.removeChild(list);
 | |
|                 newParent.addAfterAll(list);
 | |
|             }
 | |
|         }
 | |
|         else if (prev) {
 | |
|             this.updated = true;
 | |
|             parent.removeChild(list);
 | |
|             parent.addBefore(prev, list);
 | |
|         }
 | |
|         if (!this.updated) {
 | |
|             return;
 | |
|         }
 | |
|         const listStartLineAfter = root.getContentLinesRangeOf(list)[0];
 | |
|         const lineDiff = listStartLineAfter - listStartLineBefore;
 | |
|         const cursor = root.getCursor();
 | |
|         root.replaceCursor({
 | |
|             line: cursor.line + lineDiff,
 | |
|             ch: cursor.ch,
 | |
|         });
 | |
|         recalculateNumericBullets(root);
 | |
|     }
 | |
| }
 | |
| 
 | |
| class ListsMovementCommands {
 | |
|     constructor(plugin, obsidianSettings, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.obsidianSettings = obsidianSettings;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.moveListDown = (editor) => {
 | |
|             const { shouldStopPropagation } = this.operationPerformer.perform((root) => new MoveListDown(root), editor);
 | |
|             return shouldStopPropagation;
 | |
|         };
 | |
|         this.moveListUp = (editor) => {
 | |
|             const { shouldStopPropagation } = this.operationPerformer.perform((root) => new MoveListUp(root), editor);
 | |
|             return shouldStopPropagation;
 | |
|         };
 | |
|         this.indentList = (editor) => {
 | |
|             const { shouldStopPropagation } = this.operationPerformer.perform((root) => new IndentList(root, this.obsidianSettings.getDefaultIndentChars()), editor);
 | |
|             return shouldStopPropagation;
 | |
|         };
 | |
|         this.outdentList = (editor) => {
 | |
|             const { shouldStopPropagation } = this.operationPerformer.perform((root) => new OutdentList(root), editor);
 | |
|             return shouldStopPropagation;
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.addCommand({
 | |
|                 id: "move-list-item-up",
 | |
|                 icon: "arrow-up",
 | |
|                 name: "Move list and sublists up",
 | |
|                 editorCallback: createEditorCallback(this.moveListUp),
 | |
|                 hotkeys: [
 | |
|                     {
 | |
|                         modifiers: ["Mod", "Shift"],
 | |
|                         key: "ArrowUp",
 | |
|                     },
 | |
|                 ],
 | |
|             });
 | |
|             this.plugin.addCommand({
 | |
|                 id: "move-list-item-down",
 | |
|                 icon: "arrow-down",
 | |
|                 name: "Move list and sublists down",
 | |
|                 editorCallback: createEditorCallback(this.moveListDown),
 | |
|                 hotkeys: [
 | |
|                     {
 | |
|                         modifiers: ["Mod", "Shift"],
 | |
|                         key: "ArrowDown",
 | |
|                     },
 | |
|                 ],
 | |
|             });
 | |
|             this.plugin.addCommand({
 | |
|                 id: "indent-list",
 | |
|                 icon: "indent",
 | |
|                 name: "Indent the list and sublists",
 | |
|                 editorCallback: createEditorCallback(this.indentList),
 | |
|                 hotkeys: [],
 | |
|             });
 | |
|             this.plugin.addCommand({
 | |
|                 id: "outdent-list",
 | |
|                 icon: "outdent",
 | |
|                 name: "Outdent the list and sublists",
 | |
|                 editorCallback: createEditorCallback(this.outdentList),
 | |
|                 hotkeys: [],
 | |
|             });
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class DeleteTillCurrentLineContentStart {
 | |
|     constructor(root) {
 | |
|         this.root = root;
 | |
|         this.stopPropagation = false;
 | |
|         this.updated = false;
 | |
|     }
 | |
|     shouldStopPropagation() {
 | |
|         return this.stopPropagation;
 | |
|     }
 | |
|     shouldUpdate() {
 | |
|         return this.updated;
 | |
|     }
 | |
|     perform() {
 | |
|         const { root } = this;
 | |
|         if (!root.hasSingleCursor()) {
 | |
|             return;
 | |
|         }
 | |
|         this.stopPropagation = true;
 | |
|         this.updated = true;
 | |
|         const cursor = root.getCursor();
 | |
|         const list = root.getListUnderCursor();
 | |
|         const lines = list.getLinesInfo();
 | |
|         const lineNo = lines.findIndex((l) => l.from.line === cursor.line);
 | |
|         lines[lineNo].text = lines[lineNo].text.slice(cursor.ch - lines[lineNo].from.ch);
 | |
|         list.replaceLines(lines.map((l) => l.text));
 | |
|         root.replaceCursor(lines[lineNo].from);
 | |
|     }
 | |
| }
 | |
| 
 | |
| class MetaBackspaceBehaviourOverride {
 | |
|     constructor(plugin, settings, imeDetector, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.imeDetector = imeDetector;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.check = () => {
 | |
|             return (this.settings.keepCursorWithinContent !== "never" &&
 | |
|                 !this.imeDetector.isOpened());
 | |
|         };
 | |
|         this.run = (editor) => {
 | |
|             return this.operationPerformer.perform((root) => new DeleteTillCurrentLineContentStart(root), editor);
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(view.keymap.of([
 | |
|                 {
 | |
|                     mac: "m-Backspace",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|             ]));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class ObsidianOutlinerPluginSettingTab extends obsidian.PluginSettingTab {
 | |
|     constructor(app, plugin, settings) {
 | |
|         super(app, plugin);
 | |
|         this.settings = settings;
 | |
|     }
 | |
|     display() {
 | |
|         const { containerEl } = this;
 | |
|         containerEl.empty();
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Stick the cursor to the content")
 | |
|             .setDesc("Don't let the cursor move to the bullet position.")
 | |
|             .addDropdown((dropdown) => {
 | |
|             dropdown
 | |
|                 .addOptions({
 | |
|                 never: "Never",
 | |
|                 "bullet-only": "Stick cursor out of bullets",
 | |
|                 "bullet-and-checkbox": "Stick cursor out of bullets and checkboxes",
 | |
|             })
 | |
|                 .setValue(this.settings.keepCursorWithinContent)
 | |
|                 .onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.keepCursorWithinContent = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Enhance the Tab key")
 | |
|             .setDesc("Make Tab and Shift-Tab behave the same as other outliners.")
 | |
|             .addToggle((toggle) => {
 | |
|             toggle
 | |
|                 .setValue(this.settings.overrideTabBehaviour)
 | |
|                 .onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.overrideTabBehaviour = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Enhance the Enter key")
 | |
|             .setDesc("Make the Enter key behave the same as other outliners.")
 | |
|             .addToggle((toggle) => {
 | |
|             toggle
 | |
|                 .setValue(this.settings.overrideEnterBehaviour)
 | |
|                 .onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.overrideEnterBehaviour = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Vim-mode o/O inserts bullets")
 | |
|             .setDesc("Create a bullet when pressing o or O in Vim mode.")
 | |
|             .addToggle((toggle) => {
 | |
|             toggle
 | |
|                 .setValue(this.settings.overrideVimOBehaviour)
 | |
|                 .onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.overrideVimOBehaviour = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Enhance the Ctrl+A or Cmd+A behavior")
 | |
|             .setDesc("Press the hotkey once to select the current list item. Press the hotkey twice to select the entire list.")
 | |
|             .addToggle((toggle) => {
 | |
|             toggle
 | |
|                 .setValue(this.settings.overrideSelectAllBehaviour)
 | |
|                 .onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.overrideSelectAllBehaviour = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Improve the style of your lists")
 | |
|             .setDesc("Styles are only compatible with built-in Obsidian themes and may not be compatible with other themes.")
 | |
|             .addToggle((toggle) => {
 | |
|             toggle
 | |
|                 .setValue(this.settings.betterListsStyles)
 | |
|                 .onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.betterListsStyles = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Draw vertical indentation lines")
 | |
|             .addToggle((toggle) => {
 | |
|             toggle.setValue(this.settings.verticalLines).onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.verticalLines = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Vertical indentation line click action")
 | |
|             .addDropdown((dropdown) => {
 | |
|             dropdown
 | |
|                 .addOptions({
 | |
|                 none: "None",
 | |
|                 "zoom-in": "Zoom In",
 | |
|                 "toggle-folding": "Toggle Folding",
 | |
|             })
 | |
|                 .setValue(this.settings.verticalLinesAction)
 | |
|                 .onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.verticalLinesAction = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl).setName("Drag-and-Drop").addToggle((toggle) => {
 | |
|             toggle.setValue(this.settings.dragAndDrop).onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.dragAndDrop = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|         new obsidian.Setting(containerEl)
 | |
|             .setName("Debug mode")
 | |
|             .setDesc("Open DevTools (Command+Option+I or Control+Shift+I) to copy the debug logs.")
 | |
|             .addToggle((toggle) => {
 | |
|             toggle.setValue(this.settings.debug).onChange((value) => __awaiter(this, void 0, void 0, function* () {
 | |
|                 this.settings.debug = value;
 | |
|                 yield this.settings.save();
 | |
|             }));
 | |
|         });
 | |
|     }
 | |
| }
 | |
| class SettingsTab {
 | |
|     constructor(plugin, settings) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.addSettingTab(new ObsidianOutlinerPluginSettingTab(this.plugin.app, this.plugin, this.settings));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class ShiftTabBehaviourOverride {
 | |
|     constructor(plugin, imeDetector, settings, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.imeDetector = imeDetector;
 | |
|         this.settings = settings;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.check = () => {
 | |
|             return this.settings.overrideTabBehaviour && !this.imeDetector.isOpened();
 | |
|         };
 | |
|         this.run = (editor) => {
 | |
|             return this.operationPerformer.perform((root) => new OutdentList(root), editor);
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(state.Prec.highest(view.keymap.of([
 | |
|                 {
 | |
|                     key: "s-Tab",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|             ])));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class SystemInfoModal extends obsidian.Modal {
 | |
|     constructor(app, settings) {
 | |
|         super(app);
 | |
|         this.settings = settings;
 | |
|     }
 | |
|     onOpen() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.titleEl.setText("System Information");
 | |
|             // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | |
|             const app = this.app;
 | |
|             const data = {
 | |
|                 process: {
 | |
|                     arch: process.arch,
 | |
|                     platform: process.platform,
 | |
|                 },
 | |
|                 app: {
 | |
|                     internalPlugins: {
 | |
|                         config: app.internalPlugins.config,
 | |
|                     },
 | |
|                     isMobile: app.isMobile,
 | |
|                     plugins: {
 | |
|                         enabledPlugins: Array.from(app.plugins.enabledPlugins),
 | |
|                         manifests: Object.keys(app.plugins.manifests).reduce((acc, key) => {
 | |
|                             acc[key] = {
 | |
|                                 version: app.plugins.manifests[key].version,
 | |
|                             };
 | |
|                             return acc;
 | |
|                         }, {}),
 | |
|                     },
 | |
|                     vault: {
 | |
|                         config: app.vault.config,
 | |
|                     },
 | |
|                 },
 | |
|                 plugin: {
 | |
|                     settings: { values: this.settings.getValues() },
 | |
|                 },
 | |
|             };
 | |
|             const text = JSON.stringify(data, null, 2);
 | |
|             const pre = this.contentEl.createEl("pre");
 | |
|             pre.setText(text);
 | |
|             pre.setCssStyles({
 | |
|                 overflow: "scroll",
 | |
|                 maxHeight: "300px",
 | |
|             });
 | |
|             const button = this.contentEl.createEl("button");
 | |
|             button.setText("Copy and Close");
 | |
|             button.onClickEvent(() => {
 | |
|                 navigator.clipboard.writeText("```json\n" + text + "\n```");
 | |
|                 this.close();
 | |
|             });
 | |
|         });
 | |
|     }
 | |
| }
 | |
| class SystemInfo {
 | |
|     constructor(plugin, settings) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.callback = () => {
 | |
|             const modal = new SystemInfoModal(this.plugin.app, this.settings);
 | |
|             modal.open();
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.addCommand({
 | |
|                 id: "system-info",
 | |
|                 name: "Show System Info",
 | |
|                 callback: this.callback,
 | |
|                 hotkeys: [
 | |
|                     {
 | |
|                         modifiers: ["Mod", "Shift", "Alt"],
 | |
|                         key: "I",
 | |
|                     },
 | |
|                 ],
 | |
|             });
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class TabBehaviourOverride {
 | |
|     constructor(plugin, imeDetector, obsidianSettings, settings, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.imeDetector = imeDetector;
 | |
|         this.obsidianSettings = obsidianSettings;
 | |
|         this.settings = settings;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.check = () => {
 | |
|             return this.settings.overrideTabBehaviour && !this.imeDetector.isOpened();
 | |
|         };
 | |
|         this.run = (editor) => {
 | |
|             return this.operationPerformer.perform((root) => new IndentList(root, this.obsidianSettings.getDefaultIndentChars()), editor);
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.plugin.registerEditorExtension(state.Prec.highest(view.keymap.of([
 | |
|                 {
 | |
|                     key: "Tab",
 | |
|                     run: createKeymapRunCallback({
 | |
|                         check: this.check,
 | |
|                         run: this.run,
 | |
|                     }),
 | |
|                 },
 | |
|             ])));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () { });
 | |
|     }
 | |
| }
 | |
| 
 | |
| const VERTICAL_LINES_BODY_CLASS = "outliner-plugin-vertical-lines";
 | |
| class VerticalLinesPluginValue {
 | |
|     constructor(settings, obsidianSettings, parser, view) {
 | |
|         this.settings = settings;
 | |
|         this.obsidianSettings = obsidianSettings;
 | |
|         this.parser = parser;
 | |
|         this.view = view;
 | |
|         this.lineElements = [];
 | |
|         this.waitForEditor = () => {
 | |
|             const editor = getEditorFromState(this.view.state);
 | |
|             if (!editor) {
 | |
|                 setTimeout(this.waitForEditor, 0);
 | |
|                 return;
 | |
|             }
 | |
|             this.editor = editor;
 | |
|             this.scheduleRecalculate();
 | |
|         };
 | |
|         this.onScroll = (e) => {
 | |
|             const { scrollLeft, scrollTop } = e.target;
 | |
|             this.scroller.scrollTo(scrollLeft, scrollTop);
 | |
|         };
 | |
|         this.scheduleRecalculate = () => {
 | |
|             clearTimeout(this.scheduled);
 | |
|             this.scheduled = setTimeout(this.calculate, 0);
 | |
|         };
 | |
|         this.calculate = () => {
 | |
|             this.lines = [];
 | |
|             if (this.settings.verticalLines &&
 | |
|                 this.obsidianSettings.isDefaultThemeEnabled() &&
 | |
|                 this.view.viewportLineBlocks.length > 0 &&
 | |
|                 this.view.visibleRanges.length > 0) {
 | |
|                 const fromLine = this.editor.offsetToPos(this.view.viewport.from).line;
 | |
|                 const toLine = this.editor.offsetToPos(this.view.viewport.to).line;
 | |
|                 const lists = this.parser.parseRange(this.editor, fromLine, toLine);
 | |
|                 for (const list of lists) {
 | |
|                     this.lastLine = list.getContentEnd().line;
 | |
|                     for (const c of list.getChildren()) {
 | |
|                         this.recursive(c);
 | |
|                     }
 | |
|                 }
 | |
|                 this.lines.sort((a, b) => a.top === b.top ? a.left - b.left : a.top - b.top);
 | |
|             }
 | |
|             this.updateDom();
 | |
|         };
 | |
|         this.onClick = (e) => {
 | |
|             e.preventDefault();
 | |
|             const line = this.lines[Number(e.target.dataset.index)];
 | |
|             switch (this.settings.verticalLinesAction) {
 | |
|                 case "zoom-in":
 | |
|                     this.zoomIn(line);
 | |
|                     break;
 | |
|                 case "toggle-folding":
 | |
|                     this.toggleFolding(line);
 | |
|                     break;
 | |
|             }
 | |
|         };
 | |
|         this.view.scrollDOM.addEventListener("scroll", this.onScroll);
 | |
|         this.settings.onChange(this.scheduleRecalculate);
 | |
|         this.prepareDom();
 | |
|         this.waitForEditor();
 | |
|     }
 | |
|     prepareDom() {
 | |
|         this.contentContainer = document.createElement("div");
 | |
|         this.contentContainer.classList.add("outliner-plugin-list-lines-content-container");
 | |
|         this.scroller = document.createElement("div");
 | |
|         this.scroller.classList.add("outliner-plugin-list-lines-scroller");
 | |
|         this.scroller.appendChild(this.contentContainer);
 | |
|         this.view.dom.appendChild(this.scroller);
 | |
|     }
 | |
|     update(update) {
 | |
|         if (update.docChanged ||
 | |
|             update.viewportChanged ||
 | |
|             update.geometryChanged ||
 | |
|             update.transactions.some((tr) => tr.reconfigured)) {
 | |
|             this.scheduleRecalculate();
 | |
|         }
 | |
|     }
 | |
|     getNextSibling(list) {
 | |
|         let listTmp = list;
 | |
|         let p = listTmp.getParent();
 | |
|         while (p) {
 | |
|             const nextSibling = p.getNextSiblingOf(listTmp);
 | |
|             if (nextSibling) {
 | |
|                 return nextSibling;
 | |
|             }
 | |
|             listTmp = p;
 | |
|             p = listTmp.getParent();
 | |
|         }
 | |
|         return null;
 | |
|     }
 | |
|     recursive(list, parentCtx = {}) {
 | |
|         const children = list.getChildren();
 | |
|         if (children.length === 0) {
 | |
|             return;
 | |
|         }
 | |
|         const fromOffset = this.editor.posToOffset({
 | |
|             line: list.getFirstLineContentStart().line,
 | |
|             ch: list.getFirstLineIndent().length,
 | |
|         });
 | |
|         const nextSibling = this.getNextSibling(list);
 | |
|         const tillOffset = this.editor.posToOffset({
 | |
|             line: nextSibling
 | |
|                 ? nextSibling.getFirstLineContentStart().line - 1
 | |
|                 : this.lastLine,
 | |
|             ch: 0,
 | |
|         });
 | |
|         let visibleFrom = this.view.visibleRanges[0].from;
 | |
|         let visibleTo = this.view.visibleRanges[this.view.visibleRanges.length - 1].to;
 | |
|         const zoomRange = this.editor.getZoomRange();
 | |
|         if (zoomRange) {
 | |
|             visibleFrom = Math.max(visibleFrom, this.editor.posToOffset(zoomRange.from));
 | |
|             visibleTo = Math.min(visibleTo, this.editor.posToOffset(zoomRange.to));
 | |
|         }
 | |
|         if (fromOffset > visibleTo || tillOffset < visibleFrom) {
 | |
|             return;
 | |
|         }
 | |
|         const coords = this.view.coordsAtPos(fromOffset, 1);
 | |
|         if (parentCtx.rootLeft === undefined) {
 | |
|             parentCtx.rootLeft = coords.left;
 | |
|         }
 | |
|         const left = Math.floor(coords.right - parentCtx.rootLeft);
 | |
|         const top = visibleFrom > 0 && fromOffset < visibleFrom
 | |
|             ? -20
 | |
|             : this.view.lineBlockAt(fromOffset).top;
 | |
|         const bottom = tillOffset > visibleTo
 | |
|             ? this.view.lineBlockAt(visibleTo - 1).bottom
 | |
|             : this.view.lineBlockAt(tillOffset).bottom;
 | |
|         const height = bottom - top;
 | |
|         if (height > 0 && !list.isFolded()) {
 | |
|             const nextSibling = list.getParent().getNextSiblingOf(list);
 | |
|             const hasNextSibling = !!nextSibling &&
 | |
|                 this.editor.posToOffset(nextSibling.getFirstLineContentStart()) <=
 | |
|                     visibleTo;
 | |
|             this.lines.push({
 | |
|                 top,
 | |
|                 left,
 | |
|                 height: `calc(${height}px ${hasNextSibling ? "- 1.5em" : "- 2em"})`,
 | |
|                 list,
 | |
|             });
 | |
|         }
 | |
|         for (const child of children) {
 | |
|             if (!child.isEmpty()) {
 | |
|                 this.recursive(child, parentCtx);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     zoomIn(line) {
 | |
|         const editor = getEditorFromState(this.view.state);
 | |
|         editor.zoomIn(line.list.getFirstLineContentStart().line);
 | |
|     }
 | |
|     toggleFolding(line) {
 | |
|         const { list } = line;
 | |
|         if (list.isEmpty()) {
 | |
|             return;
 | |
|         }
 | |
|         let needToUnfold = true;
 | |
|         const linesToToggle = [];
 | |
|         for (const c of list.getChildren()) {
 | |
|             if (c.isEmpty()) {
 | |
|                 continue;
 | |
|             }
 | |
|             if (!c.isFolded()) {
 | |
|                 needToUnfold = false;
 | |
|             }
 | |
|             linesToToggle.push(c.getFirstLineContentStart().line);
 | |
|         }
 | |
|         const editor = getEditorFromState(this.view.state);
 | |
|         for (const l of linesToToggle) {
 | |
|             if (needToUnfold) {
 | |
|                 editor.unfold(l);
 | |
|             }
 | |
|             else {
 | |
|                 editor.fold(l);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     updateDom() {
 | |
|         const cmScroll = this.view.scrollDOM;
 | |
|         const cmContent = this.view.contentDOM;
 | |
|         const cmContentContainer = cmContent.parentElement;
 | |
|         const cmSizer = cmContentContainer.parentElement;
 | |
|         /**
 | |
|          * Obsidian can add additional elements into Content Manager.
 | |
|          * The most obvious case is the 'embedded-backlinks' core plugin that adds a menu inside a Content Manager.
 | |
|          * We must take heights of all of these elements into account
 | |
|          * to be able to calculate the correct size of lines' container.
 | |
|          */
 | |
|         let cmSizerChildrenSumHeight = 0;
 | |
|         for (let i = 0; i < cmSizer.children.length; i++) {
 | |
|             cmSizerChildrenSumHeight += cmSizer.children[i].clientHeight;
 | |
|         }
 | |
|         this.scroller.style.top = cmScroll.offsetTop + "px";
 | |
|         this.contentContainer.style.height = cmSizerChildrenSumHeight + "px";
 | |
|         this.contentContainer.style.marginLeft =
 | |
|             cmContentContainer.offsetLeft + "px";
 | |
|         this.contentContainer.style.marginTop =
 | |
|             cmContent.firstElementChild.offsetTop - 24 + "px";
 | |
|         for (let i = 0; i < this.lines.length; i++) {
 | |
|             if (this.lineElements.length === i) {
 | |
|                 const e = document.createElement("div");
 | |
|                 e.classList.add("outliner-plugin-list-line");
 | |
|                 e.dataset.index = String(i);
 | |
|                 e.addEventListener("mousedown", this.onClick);
 | |
|                 this.contentContainer.appendChild(e);
 | |
|                 this.lineElements.push(e);
 | |
|             }
 | |
|             const l = this.lines[i];
 | |
|             const e = this.lineElements[i];
 | |
|             e.style.top = l.top + "px";
 | |
|             e.style.left = l.left + "px";
 | |
|             e.style.height = l.height;
 | |
|             e.style.display = "block";
 | |
|         }
 | |
|         for (let i = this.lines.length; i < this.lineElements.length; i++) {
 | |
|             const e = this.lineElements[i];
 | |
|             e.style.top = "0px";
 | |
|             e.style.left = "0px";
 | |
|             e.style.height = "0px";
 | |
|             e.style.display = "none";
 | |
|         }
 | |
|     }
 | |
|     destroy() {
 | |
|         this.settings.removeCallback(this.scheduleRecalculate);
 | |
|         this.view.scrollDOM.removeEventListener("scroll", this.onScroll);
 | |
|         this.view.dom.removeChild(this.scroller);
 | |
|         clearTimeout(this.scheduled);
 | |
|     }
 | |
| }
 | |
| class VerticalLines {
 | |
|     constructor(plugin, settings, obsidianSettings, parser) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.obsidianSettings = obsidianSettings;
 | |
|         this.parser = parser;
 | |
|         this.updateBodyClass = () => {
 | |
|             const shouldExists = this.obsidianSettings.isDefaultThemeEnabled() &&
 | |
|                 this.settings.verticalLines;
 | |
|             const exists = document.body.classList.contains(VERTICAL_LINES_BODY_CLASS);
 | |
|             if (shouldExists && !exists) {
 | |
|                 document.body.classList.add(VERTICAL_LINES_BODY_CLASS);
 | |
|             }
 | |
|             if (!shouldExists && exists) {
 | |
|                 document.body.classList.remove(VERTICAL_LINES_BODY_CLASS);
 | |
|             }
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.updateBodyClass();
 | |
|             this.updateBodyClassInterval = window.setInterval(() => {
 | |
|                 this.updateBodyClass();
 | |
|             }, 1000);
 | |
|             this.plugin.registerEditorExtension(view.ViewPlugin.define((view) => new VerticalLinesPluginValue(this.settings, this.obsidianSettings, this.parser, view)));
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             clearInterval(this.updateBodyClassInterval);
 | |
|             document.body.classList.remove(VERTICAL_LINES_BODY_CLASS);
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class VimOBehaviourOverride {
 | |
|     constructor(plugin, settings, obsidianSettings, parser, operationPerformer) {
 | |
|         this.plugin = plugin;
 | |
|         this.settings = settings;
 | |
|         this.obsidianSettings = obsidianSettings;
 | |
|         this.parser = parser;
 | |
|         this.operationPerformer = operationPerformer;
 | |
|         this.inited = false;
 | |
|         this.handleSettingsChange = () => {
 | |
|             if (!this.settings.overrideVimOBehaviour) {
 | |
|                 return;
 | |
|             }
 | |
|             if (!window.CodeMirrorAdapter || !window.CodeMirrorAdapter.Vim) {
 | |
|                 console.error("Vim adapter not found");
 | |
|                 return;
 | |
|             }
 | |
|             const vim = window.CodeMirrorAdapter.Vim;
 | |
|             const plugin = this.plugin;
 | |
|             const parser = this.parser;
 | |
|             const obsidianSettings = this.obsidianSettings;
 | |
|             const operationPerformer = this.operationPerformer;
 | |
|             const settings = this.settings;
 | |
|             vim.defineAction("insertLineAfterBullet", (cm, operatorArgs) => {
 | |
|                 // Move the cursor to the end of the line
 | |
|                 vim.handleEx(cm, "normal! A");
 | |
|                 if (!settings.overrideVimOBehaviour) {
 | |
|                     if (operatorArgs.after) {
 | |
|                         vim.handleEx(cm, "normal! o");
 | |
|                     }
 | |
|                     else {
 | |
|                         vim.handleEx(cm, "normal! O");
 | |
|                     }
 | |
|                     vim.enterInsertMode(cm);
 | |
|                     return;
 | |
|                 }
 | |
|                 const view = plugin.app.workspace.getActiveViewOfType(obsidian.MarkdownView);
 | |
|                 const editor = new MyEditor(view.editor);
 | |
|                 const root = parser.parse(editor);
 | |
|                 if (!root) {
 | |
|                     if (operatorArgs.after) {
 | |
|                         vim.handleEx(cm, "normal! o");
 | |
|                     }
 | |
|                     else {
 | |
|                         vim.handleEx(cm, "normal! O");
 | |
|                     }
 | |
|                     vim.enterInsertMode(cm);
 | |
|                     return;
 | |
|                 }
 | |
|                 const defaultIndentChars = obsidianSettings.getDefaultIndentChars();
 | |
|                 const zoomRange = editor.getZoomRange();
 | |
|                 const getZoomRange = {
 | |
|                     getZoomRange: () => zoomRange,
 | |
|                 };
 | |
|                 const res = operationPerformer.eval(root, new CreateNewItem(root, defaultIndentChars, getZoomRange, operatorArgs.after), editor);
 | |
|                 if (res.shouldUpdate && zoomRange) {
 | |
|                     editor.tryRefreshZoom(zoomRange.from.line);
 | |
|                 }
 | |
|                 // Ensure the editor is always left in insert mode
 | |
|                 vim.enterInsertMode(cm);
 | |
|             });
 | |
|             vim.mapCommand("o", "action", "insertLineAfterBullet", {}, {
 | |
|                 isEdit: true,
 | |
|                 context: "normal",
 | |
|                 interlaceInsertRepeat: true,
 | |
|                 actionArgs: { after: true },
 | |
|             });
 | |
|             vim.mapCommand("O", "action", "insertLineAfterBullet", {}, {
 | |
|                 isEdit: true,
 | |
|                 context: "normal",
 | |
|                 interlaceInsertRepeat: true,
 | |
|                 actionArgs: { after: false },
 | |
|             });
 | |
|             this.inited = true;
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.settings.onChange(this.handleSettingsChange);
 | |
|             this.handleSettingsChange();
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             if (!this.inited) {
 | |
|                 return;
 | |
|             }
 | |
|             new obsidian.Notice(`To fully unload obsidian-outliner plugin, please restart the app`, 5000);
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| class ChangesApplicator {
 | |
|     apply(editor, prevRoot, newRoot) {
 | |
|         const changes = this.calculateChanges(editor, prevRoot, newRoot);
 | |
|         if (changes) {
 | |
|             const { replacement, changeFrom, changeTo } = changes;
 | |
|             const { unfold, fold } = this.calculateFoldingOprations(prevRoot, newRoot, changeFrom, changeTo);
 | |
|             for (const line of unfold) {
 | |
|                 editor.unfold(line);
 | |
|             }
 | |
|             editor.replaceRange(replacement, changeFrom, changeTo);
 | |
|             for (const line of fold) {
 | |
|                 editor.fold(line);
 | |
|             }
 | |
|         }
 | |
|         editor.setSelections(newRoot.getSelections());
 | |
|     }
 | |
|     calculateChanges(editor, prevRoot, newRoot) {
 | |
|         const rootRange = prevRoot.getContentRange();
 | |
|         const oldString = editor.getRange(rootRange[0], rootRange[1]);
 | |
|         const newString = newRoot.print();
 | |
|         const changeFrom = Object.assign({}, rootRange[0]);
 | |
|         const changeTo = Object.assign({}, rootRange[1]);
 | |
|         let oldTmp = oldString;
 | |
|         let newTmp = newString;
 | |
|         while (true) {
 | |
|             const nlIndex = oldTmp.lastIndexOf("\n");
 | |
|             if (nlIndex < 0) {
 | |
|                 break;
 | |
|             }
 | |
|             const oldLine = oldTmp.slice(nlIndex);
 | |
|             const newLine = newTmp.slice(-oldLine.length);
 | |
|             if (oldLine !== newLine) {
 | |
|                 break;
 | |
|             }
 | |
|             oldTmp = oldTmp.slice(0, -oldLine.length);
 | |
|             newTmp = newTmp.slice(0, -oldLine.length);
 | |
|             const nlIndex2 = oldTmp.lastIndexOf("\n");
 | |
|             changeTo.ch =
 | |
|                 nlIndex2 >= 0 ? oldTmp.length - nlIndex2 - 1 : oldTmp.length;
 | |
|             changeTo.line--;
 | |
|         }
 | |
|         while (true) {
 | |
|             const nlIndex = oldTmp.indexOf("\n");
 | |
|             if (nlIndex < 0) {
 | |
|                 break;
 | |
|             }
 | |
|             const oldLine = oldTmp.slice(0, nlIndex + 1);
 | |
|             const newLine = newTmp.slice(0, oldLine.length);
 | |
|             if (oldLine !== newLine) {
 | |
|                 break;
 | |
|             }
 | |
|             changeFrom.line++;
 | |
|             oldTmp = oldTmp.slice(oldLine.length);
 | |
|             newTmp = newTmp.slice(oldLine.length);
 | |
|         }
 | |
|         if (oldTmp === newTmp) {
 | |
|             return null;
 | |
|         }
 | |
|         return {
 | |
|             replacement: newTmp,
 | |
|             changeFrom,
 | |
|             changeTo,
 | |
|         };
 | |
|     }
 | |
|     calculateFoldingOprations(prevRoot, newRoot, changeFrom, changeTo) {
 | |
|         const changedRange = [changeFrom, changeTo];
 | |
|         const prevLists = getAllChildren(prevRoot);
 | |
|         const newLists = getAllChildren(newRoot);
 | |
|         const unfold = [];
 | |
|         const fold = [];
 | |
|         for (const prevList of prevLists.values()) {
 | |
|             if (!prevList.isFoldRoot()) {
 | |
|                 continue;
 | |
|             }
 | |
|             const newList = newLists.get(prevList.getID());
 | |
|             if (!newList) {
 | |
|                 continue;
 | |
|             }
 | |
|             const prevListRange = [
 | |
|                 prevList.getFirstLineContentStart(),
 | |
|                 prevList.getContentEndIncludingChildren(),
 | |
|             ];
 | |
|             if (isRangesIntersects(prevListRange, changedRange)) {
 | |
|                 unfold.push(prevList.getFirstLineContentStart().line);
 | |
|                 fold.push(newList.getFirstLineContentStart().line);
 | |
|             }
 | |
|         }
 | |
|         unfold.sort((a, b) => b - a);
 | |
|         fold.sort((a, b) => b - a);
 | |
|         return { unfold, fold };
 | |
|     }
 | |
| }
 | |
| function getAllChildrenReduceFn(acc, child) {
 | |
|     acc.set(child.getID(), child);
 | |
|     child.getChildren().reduce(getAllChildrenReduceFn, acc);
 | |
|     return acc;
 | |
| }
 | |
| function getAllChildren(root) {
 | |
|     return root.getChildren().reduce(getAllChildrenReduceFn, new Map());
 | |
| }
 | |
| 
 | |
| class IMEDetector {
 | |
|     constructor() {
 | |
|         this.composition = false;
 | |
|         this.onCompositionStart = () => {
 | |
|             this.composition = true;
 | |
|         };
 | |
|         this.onCompositionEnd = () => {
 | |
|             this.composition = false;
 | |
|         };
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             document.addEventListener("compositionstart", this.onCompositionStart);
 | |
|             document.addEventListener("compositionend", this.onCompositionEnd);
 | |
|         });
 | |
|     }
 | |
|     unload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             document.removeEventListener("compositionend", this.onCompositionEnd);
 | |
|             document.removeEventListener("compositionstart", this.onCompositionStart);
 | |
|         });
 | |
|     }
 | |
|     isOpened() {
 | |
|         return this.composition && obsidian.Platform.isDesktop;
 | |
|     }
 | |
| }
 | |
| 
 | |
| class Logger {
 | |
|     constructor(settings) {
 | |
|         this.settings = settings;
 | |
|     }
 | |
|     log(method, ...args) {
 | |
|         if (!this.settings.debug) {
 | |
|             return;
 | |
|         }
 | |
|         console.info(method, ...args);
 | |
|     }
 | |
|     bind(method) {
 | |
|         return (...args) => this.log(method, ...args);
 | |
|     }
 | |
| }
 | |
| 
 | |
| function getHiddenObsidianConfig(app) {
 | |
|     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | |
|     return app.vault.config;
 | |
| }
 | |
| class ObsidianSettings {
 | |
|     constructor(app) {
 | |
|         this.app = app;
 | |
|     }
 | |
|     isLegacyEditorEnabled() {
 | |
|         const config = Object.assign({ legacyEditor: false }, getHiddenObsidianConfig(this.app));
 | |
|         return config.legacyEditor;
 | |
|     }
 | |
|     isDefaultThemeEnabled() {
 | |
|         const config = Object.assign({ cssTheme: "" }, getHiddenObsidianConfig(this.app));
 | |
|         return config.cssTheme === "";
 | |
|     }
 | |
|     getTabsSettings() {
 | |
|         return Object.assign({ useTab: true, tabSize: 4 }, getHiddenObsidianConfig(this.app));
 | |
|     }
 | |
|     getFoldSettings() {
 | |
|         return Object.assign({ foldIndent: true }, getHiddenObsidianConfig(this.app));
 | |
|     }
 | |
|     getDefaultIndentChars() {
 | |
|         const { useTab, tabSize } = this.getTabsSettings();
 | |
|         return useTab ? "\t" : new Array(tabSize).fill(" ").join("");
 | |
|     }
 | |
| }
 | |
| 
 | |
| class OperationPerformer {
 | |
|     constructor(parser, changesApplicator) {
 | |
|         this.parser = parser;
 | |
|         this.changesApplicator = changesApplicator;
 | |
|     }
 | |
|     eval(root, op, editor) {
 | |
|         const prevRoot = root.clone();
 | |
|         op.perform();
 | |
|         if (op.shouldUpdate()) {
 | |
|             this.changesApplicator.apply(editor, prevRoot, root);
 | |
|         }
 | |
|         return {
 | |
|             shouldUpdate: op.shouldUpdate(),
 | |
|             shouldStopPropagation: op.shouldStopPropagation(),
 | |
|         };
 | |
|     }
 | |
|     perform(cb, editor, cursor = editor.getCursor()) {
 | |
|         const root = this.parser.parse(editor, cursor);
 | |
|         if (!root) {
 | |
|             return { shouldUpdate: false, shouldStopPropagation: false };
 | |
|         }
 | |
|         const op = cb(root);
 | |
|         return this.eval(root, op, editor);
 | |
|     }
 | |
| }
 | |
| 
 | |
| const bulletSignRe = `(?:[-*+]|\\d+\\.)`;
 | |
| const optionalCheckboxRe = `(?:${checkboxRe})?`;
 | |
| const listItemWithoutSpacesRe = new RegExp(`^${bulletSignRe}( |\t)`);
 | |
| const listItemRe = new RegExp(`^[ \t]*${bulletSignRe}( |\t)`);
 | |
| const stringWithSpacesRe = new RegExp(`^[ \t]+`);
 | |
| const parseListItemRe = new RegExp(`^([ \t]*)(${bulletSignRe})( |\t)(${optionalCheckboxRe})(.*)$`);
 | |
| class Parser {
 | |
|     constructor(logger, settings) {
 | |
|         this.logger = logger;
 | |
|         this.settings = settings;
 | |
|     }
 | |
|     parseRange(editor, fromLine = 0, toLine = editor.lastLine()) {
 | |
|         const lists = [];
 | |
|         for (let i = fromLine; i <= toLine; i++) {
 | |
|             const line = editor.getLine(i);
 | |
|             if (i === fromLine || this.isListItem(line)) {
 | |
|                 const list = this.parseWithLimits(editor, i, fromLine, toLine);
 | |
|                 if (list) {
 | |
|                     lists.push(list);
 | |
|                     i = list.getContentEnd().line;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return lists;
 | |
|     }
 | |
|     parse(editor, cursor = editor.getCursor()) {
 | |
|         return this.parseWithLimits(editor, cursor.line, 0, editor.lastLine());
 | |
|     }
 | |
|     parseWithLimits(editor, parsingStartLine, limitFrom, limitTo) {
 | |
|         const d = this.logger.bind("parseList");
 | |
|         const error = (msg) => {
 | |
|             d(msg);
 | |
|             return null;
 | |
|         };
 | |
|         const line = editor.getLine(parsingStartLine);
 | |
|         let listLookingPos = null;
 | |
|         if (this.isListItem(line)) {
 | |
|             listLookingPos = parsingStartLine;
 | |
|         }
 | |
|         else if (this.isLineWithIndent(line)) {
 | |
|             let listLookingPosSearch = parsingStartLine - 1;
 | |
|             while (listLookingPosSearch >= 0) {
 | |
|                 const line = editor.getLine(listLookingPosSearch);
 | |
|                 if (this.isListItem(line)) {
 | |
|                     listLookingPos = listLookingPosSearch;
 | |
|                     break;
 | |
|                 }
 | |
|                 else if (this.isLineWithIndent(line)) {
 | |
|                     listLookingPosSearch--;
 | |
|                 }
 | |
|                 else {
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if (listLookingPos === null) {
 | |
|             return null;
 | |
|         }
 | |
|         let listStartLine = null;
 | |
|         let listStartLineLookup = listLookingPos;
 | |
|         while (listStartLineLookup >= 0) {
 | |
|             const line = editor.getLine(listStartLineLookup);
 | |
|             if (!this.isListItem(line) && !this.isLineWithIndent(line)) {
 | |
|                 break;
 | |
|             }
 | |
|             if (this.isListItemWithoutSpaces(line)) {
 | |
|                 listStartLine = listStartLineLookup;
 | |
|                 if (listStartLineLookup <= limitFrom) {
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             listStartLineLookup--;
 | |
|         }
 | |
|         if (listStartLine === null) {
 | |
|             return null;
 | |
|         }
 | |
|         let listEndLine = listLookingPos;
 | |
|         let listEndLineLookup = listLookingPos;
 | |
|         while (listEndLineLookup <= editor.lastLine()) {
 | |
|             const line = editor.getLine(listEndLineLookup);
 | |
|             if (!this.isListItem(line) && !this.isLineWithIndent(line)) {
 | |
|                 break;
 | |
|             }
 | |
|             if (!this.isEmptyLine(line)) {
 | |
|                 listEndLine = listEndLineLookup;
 | |
|             }
 | |
|             if (listEndLineLookup >= limitTo) {
 | |
|                 listEndLine = limitTo;
 | |
|                 break;
 | |
|             }
 | |
|             listEndLineLookup++;
 | |
|         }
 | |
|         if (listStartLine > parsingStartLine || listEndLine < parsingStartLine) {
 | |
|             return null;
 | |
|         }
 | |
|         // if the last line contains only spaces and that's incorrect indent, then ignore the last line
 | |
|         // https://github.com/vslinko/obsidian-outliner/issues/368
 | |
|         if (listEndLine > listStartLine) {
 | |
|             const lastLine = editor.getLine(listEndLine);
 | |
|             if (lastLine.trim().length === 0) {
 | |
|                 const prevLine = editor.getLine(listEndLine - 1);
 | |
|                 const [, prevLineIndent] = /^(\s*)/.exec(prevLine);
 | |
|                 if (!lastLine.startsWith(prevLineIndent)) {
 | |
|                     listEndLine--;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         const root = new Root({ line: listStartLine, ch: 0 }, { line: listEndLine, ch: editor.getLine(listEndLine).length }, editor.listSelections().map((r) => ({
 | |
|             anchor: { line: r.anchor.line, ch: r.anchor.ch },
 | |
|             head: { line: r.head.line, ch: r.head.ch },
 | |
|         })));
 | |
|         let currentParent = root.getRootList();
 | |
|         let currentList = null;
 | |
|         let currentIndent = "";
 | |
|         const foldedLines = editor.getAllFoldedLines();
 | |
|         for (let l = listStartLine; l <= listEndLine; l++) {
 | |
|             const line = editor.getLine(l);
 | |
|             const matches = parseListItemRe.exec(line);
 | |
|             if (matches) {
 | |
|                 const [, indent, bullet, spaceAfterBullet] = matches;
 | |
|                 let [, , , , optionalCheckbox, content] = matches;
 | |
|                 content = optionalCheckbox + content;
 | |
|                 if (this.settings.keepCursorWithinContent !== "bullet-and-checkbox") {
 | |
|                     optionalCheckbox = "";
 | |
|                 }
 | |
|                 const compareLength = Math.min(currentIndent.length, indent.length);
 | |
|                 const indentSlice = indent.slice(0, compareLength);
 | |
|                 const currentIndentSlice = currentIndent.slice(0, compareLength);
 | |
|                 if (indentSlice !== currentIndentSlice) {
 | |
|                     const expected = currentIndentSlice
 | |
|                         .replace(/ /g, "S")
 | |
|                         .replace(/\t/g, "T");
 | |
|                     const got = indentSlice.replace(/ /g, "S").replace(/\t/g, "T");
 | |
|                     return error(`Unable to parse list: expected indent "${expected}", got "${got}"`);
 | |
|                 }
 | |
|                 if (indent.length > currentIndent.length) {
 | |
|                     currentParent = currentList;
 | |
|                     currentIndent = indent;
 | |
|                 }
 | |
|                 else if (indent.length < currentIndent.length) {
 | |
|                     while (currentParent.getFirstLineIndent().length >= indent.length &&
 | |
|                         currentParent.getParent()) {
 | |
|                         currentParent = currentParent.getParent();
 | |
|                     }
 | |
|                     currentIndent = indent;
 | |
|                 }
 | |
|                 const foldRoot = foldedLines.includes(l);
 | |
|                 currentList = new List(root, indent, bullet, optionalCheckbox, spaceAfterBullet, content, foldRoot);
 | |
|                 currentParent.addAfterAll(currentList);
 | |
|             }
 | |
|             else if (this.isLineWithIndent(line)) {
 | |
|                 if (!currentList) {
 | |
|                     return error(`Unable to parse list: expected list item, got empty line`);
 | |
|                 }
 | |
|                 const indentToCheck = currentList.getNotesIndent() || currentIndent;
 | |
|                 if (line.indexOf(indentToCheck) !== 0) {
 | |
|                     const expected = indentToCheck.replace(/ /g, "S").replace(/\t/g, "T");
 | |
|                     const got = line
 | |
|                         .match(/^[ \t]*/)[0]
 | |
|                         .replace(/ /g, "S")
 | |
|                         .replace(/\t/g, "T");
 | |
|                     return error(`Unable to parse list: expected indent "${expected}", got "${got}"`);
 | |
|                 }
 | |
|                 if (!currentList.getNotesIndent()) {
 | |
|                     const matches = line.match(/^[ \t]+/);
 | |
|                     if (!matches || matches[0].length <= currentIndent.length) {
 | |
|                         if (/^\s+$/.test(line)) {
 | |
|                             continue;
 | |
|                         }
 | |
|                         return error(`Unable to parse list: expected some indent, got no indent`);
 | |
|                     }
 | |
|                     currentList.setNotesIndent(matches[0]);
 | |
|                 }
 | |
|                 currentList.addLine(line.slice(currentList.getNotesIndent().length));
 | |
|             }
 | |
|             else {
 | |
|                 return error(`Unable to parse list: expected list item or note, got "${line}"`);
 | |
|             }
 | |
|         }
 | |
|         return root;
 | |
|     }
 | |
|     isEmptyLine(line) {
 | |
|         return line.length === 0;
 | |
|     }
 | |
|     isLineWithIndent(line) {
 | |
|         return stringWithSpacesRe.test(line);
 | |
|     }
 | |
|     isListItem(line) {
 | |
|         return listItemRe.test(line);
 | |
|     }
 | |
|     isListItemWithoutSpaces(line) {
 | |
|         return listItemWithoutSpacesRe.test(line);
 | |
|     }
 | |
| }
 | |
| 
 | |
| const DEFAULT_SETTINGS = {
 | |
|     styleLists: true,
 | |
|     debug: false,
 | |
|     stickCursor: "bullet-and-checkbox",
 | |
|     betterEnter: true,
 | |
|     betterVimO: true,
 | |
|     betterTab: true,
 | |
|     selectAll: true,
 | |
|     listLines: false,
 | |
|     listLineAction: "toggle-folding",
 | |
|     dnd: true,
 | |
|     previousRelease: null,
 | |
| };
 | |
| class Settings {
 | |
|     constructor(storage) {
 | |
|         this.storage = storage;
 | |
|         this.callbacks = new Set();
 | |
|     }
 | |
|     get keepCursorWithinContent() {
 | |
|         // Adaptor for users migrating from older version of the plugin.
 | |
|         if (this.values.stickCursor === true) {
 | |
|             return "bullet-and-checkbox";
 | |
|         }
 | |
|         else if (this.values.stickCursor === false) {
 | |
|             return "never";
 | |
|         }
 | |
|         return this.values.stickCursor;
 | |
|     }
 | |
|     set keepCursorWithinContent(value) {
 | |
|         this.set("stickCursor", value);
 | |
|     }
 | |
|     get overrideTabBehaviour() {
 | |
|         return this.values.betterTab;
 | |
|     }
 | |
|     set overrideTabBehaviour(value) {
 | |
|         this.set("betterTab", value);
 | |
|     }
 | |
|     get overrideEnterBehaviour() {
 | |
|         return this.values.betterEnter;
 | |
|     }
 | |
|     set overrideEnterBehaviour(value) {
 | |
|         this.set("betterEnter", value);
 | |
|     }
 | |
|     get overrideVimOBehaviour() {
 | |
|         return this.values.betterVimO;
 | |
|     }
 | |
|     set overrideVimOBehaviour(value) {
 | |
|         this.set("betterVimO", value);
 | |
|     }
 | |
|     get overrideSelectAllBehaviour() {
 | |
|         return this.values.selectAll;
 | |
|     }
 | |
|     set overrideSelectAllBehaviour(value) {
 | |
|         this.set("selectAll", value);
 | |
|     }
 | |
|     get betterListsStyles() {
 | |
|         return this.values.styleLists;
 | |
|     }
 | |
|     set betterListsStyles(value) {
 | |
|         this.set("styleLists", value);
 | |
|     }
 | |
|     get verticalLines() {
 | |
|         return this.values.listLines;
 | |
|     }
 | |
|     set verticalLines(value) {
 | |
|         this.set("listLines", value);
 | |
|     }
 | |
|     get verticalLinesAction() {
 | |
|         return this.values.listLineAction;
 | |
|     }
 | |
|     set verticalLinesAction(value) {
 | |
|         this.set("listLineAction", value);
 | |
|     }
 | |
|     get dragAndDrop() {
 | |
|         return this.values.dnd;
 | |
|     }
 | |
|     set dragAndDrop(value) {
 | |
|         this.set("dnd", value);
 | |
|     }
 | |
|     get debug() {
 | |
|         return this.values.debug;
 | |
|     }
 | |
|     set debug(value) {
 | |
|         this.set("debug", value);
 | |
|     }
 | |
|     get previousRelease() {
 | |
|         return this.values.previousRelease;
 | |
|     }
 | |
|     set previousRelease(value) {
 | |
|         this.set("previousRelease", value);
 | |
|     }
 | |
|     onChange(cb) {
 | |
|         this.callbacks.add(cb);
 | |
|     }
 | |
|     removeCallback(cb) {
 | |
|         this.callbacks.delete(cb);
 | |
|     }
 | |
|     reset() {
 | |
|         for (const [k, v] of Object.entries(DEFAULT_SETTINGS)) {
 | |
|             this.set(k, v);
 | |
|         }
 | |
|     }
 | |
|     load() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.values = Object.assign({}, DEFAULT_SETTINGS, yield this.storage.loadData());
 | |
|         });
 | |
|     }
 | |
|     save() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             yield this.storage.saveData(this.values);
 | |
|         });
 | |
|     }
 | |
|     getValues() {
 | |
|         return Object.assign({}, this.values);
 | |
|     }
 | |
|     set(key, value) {
 | |
|         this.values[key] = value;
 | |
|         for (const cb of this.callbacks) {
 | |
|             cb();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| class ObsidianOutlinerPlugin extends obsidian.Plugin {
 | |
|     onload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             console.log(`Loading obsidian-outliner`);
 | |
|             yield this.prepareSettings();
 | |
|             this.obsidianSettings = new ObsidianSettings(this.app);
 | |
|             this.logger = new Logger(this.settings);
 | |
|             this.parser = new Parser(this.logger, this.settings);
 | |
|             this.changesApplicator = new ChangesApplicator();
 | |
|             this.operationPerformer = new OperationPerformer(this.parser, this.changesApplicator);
 | |
|             this.imeDetector = new IMEDetector();
 | |
|             yield this.imeDetector.load();
 | |
|             this.features = [
 | |
|                 // service features
 | |
|                 // new ReleaseNotesAnnouncement(this, this.settings),
 | |
|                 new SettingsTab(this, this.settings),
 | |
|                 new SystemInfo(this, this.settings),
 | |
|                 // general features
 | |
|                 new ListsMovementCommands(this, this.obsidianSettings, this.operationPerformer),
 | |
|                 new ListsFoldingCommands(this, this.obsidianSettings),
 | |
|                 // features based on settings.keepCursorWithinContent
 | |
|                 new EditorSelectionsBehaviourOverride(this, this.settings, this.parser, this.operationPerformer),
 | |
|                 new ArrowLeftAndCtrlArrowLeftBehaviourOverride(this, this.settings, this.imeDetector, this.operationPerformer),
 | |
|                 new BackspaceBehaviourOverride(this, this.settings, this.imeDetector, this.operationPerformer),
 | |
|                 new MetaBackspaceBehaviourOverride(this, this.settings, this.imeDetector, this.operationPerformer),
 | |
|                 new DeleteBehaviourOverride(this, this.settings, this.imeDetector, this.operationPerformer),
 | |
|                 // features based on settings.overrideTabBehaviour
 | |
|                 new TabBehaviourOverride(this, this.imeDetector, this.obsidianSettings, this.settings, this.operationPerformer),
 | |
|                 new ShiftTabBehaviourOverride(this, this.imeDetector, this.settings, this.operationPerformer),
 | |
|                 // features based on settings.overrideEnterBehaviour
 | |
|                 new EnterBehaviourOverride(this, this.settings, this.imeDetector, this.obsidianSettings, this.parser, this.operationPerformer),
 | |
|                 // features based on settings.overrideVimOBehaviour
 | |
|                 new VimOBehaviourOverride(this, this.settings, this.obsidianSettings, this.parser, this.operationPerformer),
 | |
|                 // features based on settings.overrideSelectAllBehaviour
 | |
|                 new CtrlAAndCmdABehaviourOverride(this, this.settings, this.imeDetector, this.operationPerformer),
 | |
|                 // features based on settings.betterListsStyles
 | |
|                 new BetterListsStyles(this.settings, this.obsidianSettings),
 | |
|                 // features based on settings.verticalLines
 | |
|                 new VerticalLines(this, this.settings, this.obsidianSettings, this.parser),
 | |
|                 // features based on settings.dragAndDrop
 | |
|                 new DragAndDrop(this, this.settings, this.obsidianSettings, this.parser, this.operationPerformer),
 | |
|             ];
 | |
|             for (const feature of this.features) {
 | |
|                 yield feature.load();
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     onunload() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             console.log(`Unloading obsidian-outliner`);
 | |
|             yield this.imeDetector.unload();
 | |
|             for (const feature of this.features) {
 | |
|                 yield feature.unload();
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     prepareSettings() {
 | |
|         return __awaiter(this, void 0, void 0, function* () {
 | |
|             this.settings = new Settings(this);
 | |
|             yield this.settings.load();
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| module.exports = ObsidianOutlinerPlugin;
 | |
| 
 | |
| 
 | |
| /* nosourcemap */ |