Andley_BG4e/.obsidian/plugins/obsidian-tidy-footnotes/main.js

205 lines
7.9 KiB
JavaScript

'use strict';
var obsidian = require('obsidian');
/*! *****************************************************************************
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.
***************************************************************************** */
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());
});
}
const reKey = /\[\^(.+?(?=\]))\]/gi;
const reDefinition = /\[\^(.+)\]\:/;
// https://stackoverflow.com/a/1830844
function isNumeric(value) {
return !isNaN(value - parseFloat(value));
}
function tidyFootnotes(editor) {
let markers = [];
let definitions = new Map();
let firstDefinitionLine = -1;
let definitionsIndexed = new Map();
// Iterate through each line
const lineCount = editor.lineCount();
let prevKey = '';
for (let i = 0; i < lineCount; i++) {
const line = editor.getLine(i);
let isDefinition = false;
let match;
if (prevKey.length) {
const hasIndent = /^[ \t]/.test(line);
const isLastLine = i === (lineCount - 1);
if (hasIndent || (line.length === 0 && !isLastLine)) {
// Append line to the previous footnote definition
const value = definitions.get(prevKey);
definitions.set(prevKey, value + "\n" + line);
markers[markers.length - 1].length++;
continue;
}
else {
prevKey = '';
}
}
// Look for footnote definition
while ((match = reDefinition.exec(line)) !== null) {
if (match.length < 1)
return;
isDefinition = true;
// Remember definition and where it is
let key = match[1];
let value = line.substring(match[0].length);
definitions.set(key, value);
prevKey = key;
let marker = {
key,
line: i,
index: 0,
length: 0,
isDefinition: true
};
markers.push(marker);
// Remember first definition line to insert combined list later
if (firstDefinitionLine === -1) {
firstDefinitionLine = i;
}
break;
}
if (isDefinition)
continue;
// Look for footnote key
while ((match = reKey.exec(line)) !== null) {
if (match.length < 1)
return;
// Remember where footnote key is
let key = match[1];
let marker = {
key,
line: i,
index: match.index,
length: match[0].length,
isDefinition: false
};
markers.push(marker);
if (!definitionsIndexed.has(key)) {
// Add key into index
definitionsIndexed.set(key, {
key,
newKey: key,
isNumber: isNumeric(key),
value: ''
});
}
}
}
// Assign definition to key in index
// If definition has no key, it will be appended with its current key
definitions.forEach((value, key) => {
definitionsIndexed.set(key, {
key,
newKey: key,
isNumber: isNumeric(key),
value
});
});
// Re-index numbers and construct combined definitions output
let count = 1;
let definitionsStr = '';
definitionsIndexed.forEach((definition, marker) => {
let key = definition.key;
if (definition.isNumber) {
const current = definitionsIndexed.get(marker);
key = count.toString();
definitionsIndexed.set(marker, Object.assign(Object.assign({}, current), { newKey: key }));
count++;
}
definitionsStr += `[^${key}]:${definition.value}\n`;
});
const markersCount = markers.length;
for (let i = markersCount - 1; i >= 0; i--) {
const marker = markers[i];
const markerLine = marker.line;
if (marker.isDefinition) {
let rangeStart, rangeEnd;
const lineEnd = markerLine + 1 + marker.length;
if (lineEnd === editor.lineCount()) {
// Replace from previous to current line to fix CodeMirror 6 error
rangeStart = { line: markerLine, ch: 0 };
rangeEnd = { line: lineEnd - 1, ch: Infinity };
}
else {
// Replace from current to next line
rangeStart = { line: markerLine, ch: 0 };
rangeEnd = { line: lineEnd, ch: 0 };
}
if (markerLine === firstDefinitionLine) {
// Replace first definition line with list of indexed definitions
editor.replaceRange(definitionsStr, rangeStart, rangeEnd);
continue;
}
// Remove line(s)
editor.replaceRange('', rangeStart, rangeEnd);
continue;
}
// Check if key has changed
const definition = definitionsIndexed.get(marker.key);
const newKey = definition.newKey;
if (marker.key === newKey)
continue;
// Replace footnote key in line with the new one
const line = editor.getLine(markerLine);
const prefix = line.substring(0, marker.index);
const newMarker = `[^${newKey}]`;
const suffix = line.substr(marker.index + marker.length);
const newLine = prefix + newMarker + suffix;
editor.replaceRange(newLine, { line: markerLine, ch: 0 }, { line: markerLine, ch: Infinity });
}
if (firstDefinitionLine == -1) {
// If there are no definitions, add definitions list at the end
const lineCount = editor.lineCount();
editor.replaceRange("\n\n" + definitionsStr, { line: lineCount, ch: 0 }, { line: lineCount, ch: Infinity });
}
// console.log(markers, definitions, definitionsIndexed, definitionsStr);
}
class TidyFootnotes extends obsidian.Plugin {
onload() {
return __awaiter(this, void 0, void 0, function* () {
this.addCommand({
id: 'tidy-footnotes',
name: 'Tidy Footnotes',
checkCallback: (checking) => {
// Ensure the active view is a Markdown editor
const view = this.app.workspace.getActiveViewOfType(obsidian.MarkdownView);
if (checking)
return !!view;
if (!view || view.sourceMode == undefined)
return false;
let editor = view.editor;
tidyFootnotes(editor);
}
});
});
}
}
module.exports = TidyFootnotes;