2521 lines
77 KiB
JavaScript
2521 lines
77 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
var __defProp = Object.defineProperty;
|
|
var __defProps = Object.defineProperties;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __spreadValues = (a, b) => {
|
|
for (var prop in b || (b = {}))
|
|
if (__hasOwnProp.call(b, prop))
|
|
__defNormalProp(a, prop, b[prop]);
|
|
if (__getOwnPropSymbols)
|
|
for (var prop of __getOwnPropSymbols(b)) {
|
|
if (__propIsEnum.call(b, prop))
|
|
__defNormalProp(a, prop, b[prop]);
|
|
}
|
|
return a;
|
|
};
|
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
var __async = (__this, __arguments, generator) => {
|
|
return new Promise((resolve, reject) => {
|
|
var fulfilled = (value) => {
|
|
try {
|
|
step(generator.next(value));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
};
|
|
var rejected = (value) => {
|
|
try {
|
|
step(generator.throw(value));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
};
|
|
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
});
|
|
};
|
|
|
|
// src/main.ts
|
|
var main_exports = {};
|
|
__export(main_exports, {
|
|
default: () => CompletrPlugin
|
|
});
|
|
module.exports = __toCommonJS(main_exports);
|
|
var import_obsidian5 = require("obsidian");
|
|
|
|
// src/snippet_manager.ts
|
|
var import_view2 = require("@codemirror/view");
|
|
|
|
// src/editor_helpers.ts
|
|
function posFromIndex(doc, offset) {
|
|
let line = doc.lineAt(offset);
|
|
return { line: line.number - 1, ch: offset - line.from };
|
|
}
|
|
function indexFromPos(doc, pos) {
|
|
const ch = pos.ch;
|
|
const line = doc.line(pos.line + 1);
|
|
return Math.min(line.from + Math.max(0, ch), line.to);
|
|
}
|
|
function editorToCodeMirrorState(editor) {
|
|
return editor.cm.state;
|
|
}
|
|
function editorToCodeMirrorView(editor) {
|
|
return editor.cm;
|
|
}
|
|
function maybeLowerCase(str, lowerCase) {
|
|
return lowerCase ? str.toLowerCase() : str;
|
|
}
|
|
function matchWordBackwards(editor, cursor, charPredicate, maxLookBackDistance = 50) {
|
|
let query = "", separatorChar = null;
|
|
let lookBackEnd = Math.max(0, cursor.ch - maxLookBackDistance);
|
|
for (let i = cursor.ch - 1; i >= lookBackEnd; i--) {
|
|
const prevChar = editor.getRange(__spreadProps(__spreadValues({}, cursor), { ch: i }), __spreadProps(__spreadValues({}, cursor), { ch: i + 1 }));
|
|
if (!charPredicate(prevChar)) {
|
|
separatorChar = prevChar;
|
|
break;
|
|
}
|
|
query = prevChar + query;
|
|
}
|
|
return { query, separatorChar };
|
|
}
|
|
function isInFrontMatterBlock(editor, pos) {
|
|
if (pos.line === 0)
|
|
return false;
|
|
const bounds = getFrontMatterBounds(editor);
|
|
if (!bounds)
|
|
return false;
|
|
return pos.line > bounds.startLine && pos.line < bounds.endLine;
|
|
}
|
|
function getFrontMatterBounds(editor) {
|
|
let startLine = -1;
|
|
for (let i = 0; i < Math.min(5, editor.lastLine()); i++) {
|
|
if (editor.getLine(i) !== "---")
|
|
continue;
|
|
startLine = i;
|
|
break;
|
|
}
|
|
if (startLine === -1)
|
|
return null;
|
|
let endLine = -1;
|
|
for (let i = startLine + 1; i <= Math.min(50, editor.lastLine()); i++) {
|
|
if (editor.getLine(i) !== "---")
|
|
continue;
|
|
endLine = i;
|
|
break;
|
|
}
|
|
if (endLine === -1)
|
|
return null;
|
|
return { startLine, endLine };
|
|
}
|
|
var _BlockType = class {
|
|
constructor(c, isMultiLine, otherType0 = null) {
|
|
this.c = c;
|
|
this.isMultiLine = isMultiLine;
|
|
this.otherType0 = otherType0;
|
|
}
|
|
get isDollarBlock() {
|
|
return this === _BlockType.DOLLAR_SINGLE || this === _BlockType.DOLLAR_MULTI;
|
|
}
|
|
get isCodeBlock() {
|
|
return !this.isDollarBlock;
|
|
}
|
|
get otherType() {
|
|
return this.otherType0;
|
|
}
|
|
};
|
|
var BlockType = _BlockType;
|
|
BlockType.DOLLAR_MULTI = new _BlockType("$$", true);
|
|
BlockType.DOLLAR_SINGLE = new _BlockType("$", false, _BlockType.DOLLAR_MULTI);
|
|
BlockType.CODE_MULTI = new _BlockType("```", true);
|
|
BlockType.CODE_SINGLE = new _BlockType("`", false, _BlockType.CODE_MULTI);
|
|
(() => {
|
|
_BlockType.DOLLAR_MULTI.otherType0 = _BlockType.DOLLAR_SINGLE;
|
|
_BlockType.CODE_MULTI.otherType0 = _BlockType.CODE_SINGLE;
|
|
})();
|
|
BlockType.SINGLE_TYPES = [_BlockType.DOLLAR_SINGLE, _BlockType.CODE_SINGLE];
|
|
function getLatexBlockType(editor, cursorPos, triggerInCodeBlocks) {
|
|
var _a;
|
|
const frontMatterBounds = (_a = getFrontMatterBounds(editor)) != null ? _a : { startLine: -1, endLine: -1 };
|
|
const blockTypeStack = [];
|
|
for (let lineIndex = Math.max(0, cursorPos.line - 1e3); lineIndex <= cursorPos.line; lineIndex++) {
|
|
if (lineIndex >= frontMatterBounds.startLine && lineIndex <= frontMatterBounds.endLine)
|
|
continue;
|
|
const line = editor.getLine(lineIndex);
|
|
for (let j = cursorPos.line == lineIndex ? cursorPos.ch - 1 : line.length - 1; j >= 0; j--) {
|
|
const currentChar = line.charAt(j);
|
|
let matchingBlockType = BlockType.SINGLE_TYPES.find((b) => b.c.charAt(0) === currentChar);
|
|
if (!matchingBlockType || line.charAt(Math.max(0, j - 1)) === "\\")
|
|
continue;
|
|
const multiTypeLength = matchingBlockType.otherType.c.length;
|
|
const isDouble = j + 1 >= multiTypeLength && substringMatches(line, matchingBlockType.otherType.c, j - multiTypeLength + 1);
|
|
if (isDouble) {
|
|
j -= multiTypeLength - 1;
|
|
matchingBlockType = matchingBlockType.otherType;
|
|
}
|
|
blockTypeStack.push({ type: matchingBlockType, line: lineIndex });
|
|
}
|
|
}
|
|
if (blockTypeStack.length < 1)
|
|
return null;
|
|
let currentIndex = 0;
|
|
while (true) {
|
|
if (currentIndex >= blockTypeStack.length)
|
|
return null;
|
|
const currentBlock = blockTypeStack[currentIndex];
|
|
const otherBlockIndex = findIndex(blockTypeStack, ({ type }) => type === currentBlock.type, currentIndex + 1);
|
|
if (otherBlockIndex === -1) {
|
|
if (!triggerInCodeBlocks && currentBlock.type.isCodeBlock)
|
|
return null;
|
|
if (currentBlock.type.isCodeBlock || currentBlock.type === BlockType.DOLLAR_SINGLE && currentBlock.line !== cursorPos.line) {
|
|
currentIndex++;
|
|
continue;
|
|
}
|
|
return currentBlock.type;
|
|
} else {
|
|
currentIndex = otherBlockIndex + 1;
|
|
}
|
|
}
|
|
}
|
|
function findIndex(arr, predicate, fromIndex) {
|
|
for (let i = fromIndex; i < arr.length; i++) {
|
|
if (predicate(arr[i]))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
function substringMatches(str, toMatch, from) {
|
|
for (let i = from; i < from + toMatch.length - 1; i++) {
|
|
if (str.charAt(i) !== toMatch.charAt(i - from))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// src/marker_state_field.ts
|
|
var import_state = require("@codemirror/state");
|
|
var import_view = require("@codemirror/view");
|
|
var addMark = import_state.StateEffect.define();
|
|
var clearMarks = import_state.StateEffect.define();
|
|
var removeMarkBySpecAttribute = import_state.StateEffect.define();
|
|
var markerStateField = import_state.StateField.define({
|
|
create() {
|
|
return import_view.Decoration.none;
|
|
},
|
|
update(value, tr) {
|
|
value = value.map(tr.changes);
|
|
for (let effect of tr.effects) {
|
|
if (effect.is(addMark))
|
|
value = value.update({ add: [effect.value] });
|
|
else if (effect.is(clearMarks))
|
|
value = value.update({ filter: () => false });
|
|
else if (effect.is(removeMarkBySpecAttribute))
|
|
value = value.update({ filter: (from, to, ref) => ref.spec[effect.value.attribute] !== effect.value[effect.value.attribute] });
|
|
}
|
|
return value;
|
|
},
|
|
provide: (f) => import_view.EditorView.decorations.from(f)
|
|
});
|
|
|
|
// src/snippet_manager.ts
|
|
var COLORS = ["lightskyblue", "orange", "lime", "pink", "cornsilk", "magenta", "navajowhite"];
|
|
var PlaceholderReference = class {
|
|
constructor(editor) {
|
|
this.editor = editor;
|
|
}
|
|
get marker() {
|
|
const state = editorToCodeMirrorState(this.editor);
|
|
const iter = state.field(markerStateField).iter();
|
|
while (iter.value) {
|
|
if (iter.value.spec.reference === this) {
|
|
return {
|
|
from: iter.from,
|
|
to: iter.to,
|
|
value: iter.value
|
|
};
|
|
}
|
|
iter.next();
|
|
}
|
|
return null;
|
|
}
|
|
removeFromEditor() {
|
|
editorToCodeMirrorView(this.editor).dispatch({
|
|
effects: removeMarkBySpecAttribute.of({ attribute: "reference", reference: this })
|
|
});
|
|
}
|
|
};
|
|
var SnippetManager = class {
|
|
constructor() {
|
|
this.currentPlaceholderReferences = [];
|
|
}
|
|
handleSnippet(value, start, editor) {
|
|
let colorIndex = 0;
|
|
for (; colorIndex < COLORS.length; colorIndex++) {
|
|
if (!this.currentPlaceholderReferences.find((p) => p.marker.value.spec.attributes.class.endsWith(colorIndex + "")))
|
|
break;
|
|
}
|
|
if (colorIndex === COLORS.length) {
|
|
console.log("Completr: No colors left for snippet, using random color");
|
|
colorIndex = Math.floor(Math.random() * COLORS.length);
|
|
}
|
|
const editorView = editorToCodeMirrorView(editor);
|
|
const lines = value.split("\n");
|
|
for (let lineIndex = lines.length - 1; lineIndex >= 0; lineIndex--) {
|
|
const line = lines[lineIndex];
|
|
for (let i = line.length - 1; i >= 0; i--) {
|
|
const c = line.charAt(i);
|
|
if (c !== "#" && c !== "~")
|
|
continue;
|
|
const lineBaseOffset = lineIndex === 0 ? start.ch : 0;
|
|
if (c === "~") {
|
|
const cursorPos = { line: start.line + lineIndex, ch: lineBaseOffset + i };
|
|
editor.setCursor(cursorPos);
|
|
editor.replaceRange("", cursorPos, __spreadProps(__spreadValues({}, cursorPos), { ch: cursorPos.ch + 1 }));
|
|
continue;
|
|
}
|
|
const reference = new PlaceholderReference(editor);
|
|
let mark = import_view2.Decoration.mark({
|
|
inclusive: true,
|
|
attributes: {
|
|
style: "border-width: 1px 0 1px 0;border-style: solid;",
|
|
class: "completr-suggestion-placeholder" + colorIndex
|
|
},
|
|
reference
|
|
}).range(
|
|
indexFromPos(editorView.state.doc, { line: start.line + lineIndex, ch: lineBaseOffset + i }),
|
|
indexFromPos(editorView.state.doc, { line: start.line + lineIndex, ch: lineBaseOffset + i + 1 })
|
|
);
|
|
editorView.dispatch({ effects: addMark.of(mark) });
|
|
this.currentPlaceholderReferences.unshift(reference);
|
|
}
|
|
}
|
|
this.selectMarker(this.currentPlaceholderReferences[0]);
|
|
}
|
|
consumeAndGotoNextMarker(editor) {
|
|
const oldPlaceholder = this.currentPlaceholderReferences.shift();
|
|
const oldRange = SnippetManager.rangeFromPlaceholder(oldPlaceholder);
|
|
oldPlaceholder.removeFromEditor();
|
|
if (this.currentPlaceholderReferences.length === 0)
|
|
return false;
|
|
const placeholder = this.currentPlaceholderReferences[0];
|
|
const newRange = SnippetManager.rangeFromPlaceholder(placeholder);
|
|
if (!newRange)
|
|
return false;
|
|
if (newRange.from.ch <= oldRange.from.ch && newRange.to.ch >= oldRange.to.ch) {
|
|
editor.setCursor(__spreadValues({}, newRange.to));
|
|
} else {
|
|
this.selectMarker(placeholder);
|
|
}
|
|
return true;
|
|
}
|
|
placeholderAtPos(pos) {
|
|
for (let i = this.currentPlaceholderReferences.length - 1; i >= 0; i--) {
|
|
const placeholder = this.currentPlaceholderReferences[i];
|
|
const range = SnippetManager.rangeFromPlaceholder(placeholder);
|
|
if (!range) {
|
|
this.currentPlaceholderReferences.slice(i, 1);
|
|
continue;
|
|
}
|
|
if (range.from.ch <= pos.ch && range.to.ch >= pos.ch)
|
|
return placeholder;
|
|
}
|
|
return null;
|
|
}
|
|
selectMarker(reference) {
|
|
if (!reference)
|
|
return;
|
|
const from = posFromIndex(editorToCodeMirrorState(reference.editor).doc, reference.marker.from);
|
|
reference.editor.setSelection(from, __spreadProps(__spreadValues({}, from), { ch: from.ch + 1 }));
|
|
}
|
|
clearAllPlaceholders() {
|
|
if (this.currentPlaceholderReferences.length === 0)
|
|
return;
|
|
const firstRef = this.currentPlaceholderReferences[0];
|
|
const view = editorToCodeMirrorView(firstRef.editor);
|
|
view.dispatch({
|
|
effects: clearMarks.of(null)
|
|
});
|
|
this.currentPlaceholderReferences = [];
|
|
}
|
|
static rangeFromPlaceholder(reference) {
|
|
const marker = reference.marker;
|
|
if (!marker)
|
|
return null;
|
|
return {
|
|
from: posFromIndex(editorToCodeMirrorState(reference.editor).doc, marker.from),
|
|
to: posFromIndex(editorToCodeMirrorState(reference.editor).doc, marker.to)
|
|
};
|
|
}
|
|
onunload() {
|
|
this.clearAllPlaceholders();
|
|
}
|
|
};
|
|
|
|
// src/provider/provider.ts
|
|
function getSuggestionDisplayName(suggestion, lowerCase = false) {
|
|
const res = typeof suggestion === "string" ? suggestion : suggestion.displayName;
|
|
return maybeLowerCase(res, lowerCase);
|
|
}
|
|
function getSuggestionReplacement(suggestion) {
|
|
return typeof suggestion === "string" ? suggestion : suggestion.replacement;
|
|
}
|
|
|
|
// src/provider/latex_provider.ts
|
|
var import_obsidian = require("obsidian");
|
|
|
|
// src/provider/blacklist.ts
|
|
var BLACKLIST_PATH = ".obsidian/plugins/obsidian-completr/blacklisted_suggestions.txt";
|
|
var NEW_LINE_REGEX = /\r?\n/;
|
|
var SuggestionBlacklist = new class {
|
|
constructor() {
|
|
this.blacklist = /* @__PURE__ */ new Set();
|
|
}
|
|
add(suggestion) {
|
|
this.blacklist.add(getSuggestionDisplayName(suggestion));
|
|
}
|
|
has(suggestion) {
|
|
return this.blacklist.has(getSuggestionDisplayName(suggestion));
|
|
}
|
|
filter(suggestions) {
|
|
if (this.blacklist.size < 1)
|
|
return suggestions;
|
|
return suggestions.filter((s) => !this.blacklist.has(getSuggestionDisplayName(s)));
|
|
}
|
|
saveData(vault) {
|
|
return __async(this, null, function* () {
|
|
yield vault.adapter.write(BLACKLIST_PATH, [...this.blacklist].join("\n"));
|
|
});
|
|
}
|
|
loadData(vault) {
|
|
return __async(this, null, function* () {
|
|
if (!(yield vault.adapter.exists(BLACKLIST_PATH)))
|
|
return;
|
|
const contents = (yield vault.adapter.read(BLACKLIST_PATH)).split(NEW_LINE_REGEX);
|
|
for (let word of contents) {
|
|
if (!word)
|
|
continue;
|
|
this.add(word);
|
|
}
|
|
});
|
|
}
|
|
}();
|
|
|
|
// src/provider/latex_provider.ts
|
|
function substringUntil(str, delimiter) {
|
|
let index = str.indexOf(delimiter);
|
|
if (index === -1)
|
|
return str;
|
|
return str.substring(0, index);
|
|
}
|
|
var LATEX_COMMANDS_PATH = ".obsidian/plugins/obsidian-completr/latex_commands.json";
|
|
var LatexSuggestionProvider = class {
|
|
constructor() {
|
|
this.loadedCommands = [];
|
|
}
|
|
getSuggestions(context, settings) {
|
|
if (!settings.latexProviderEnabled || !context.query || context.query.length < settings.latexMinWordTriggerLength)
|
|
return [];
|
|
let editor = context.editor;
|
|
const latexBlockType = getLatexBlockType(editor, context.start, settings.latexTriggerInCodeBlocks);
|
|
const isSingleBlock = latexBlockType === BlockType.DOLLAR_SINGLE;
|
|
if (!latexBlockType)
|
|
return [];
|
|
const query = maybeLowerCase(context.query, settings.latexIgnoreCase);
|
|
const isSeparatorBackslash = context.separatorChar === "\\";
|
|
return this.loadedCommands.filter((s) => getSuggestionDisplayName(s, settings.latexIgnoreCase).contains(query)).map((s) => {
|
|
let replacement = getSuggestionReplacement(s);
|
|
replacement = isSeparatorBackslash ? replacement.substring(1) : replacement;
|
|
replacement = isSingleBlock ? replacement.replace(/\n/g, "") : replacement;
|
|
return {
|
|
displayName: getSuggestionDisplayName(s),
|
|
replacement,
|
|
priority: getSuggestionDisplayName(s, settings.latexIgnoreCase).indexOf(query)
|
|
};
|
|
}).sort((a, b) => {
|
|
let val = a.priority - b.priority;
|
|
if (val == 0)
|
|
val = substringUntil(a.displayName, "{").length - substringUntil(b.displayName, "{").length;
|
|
return val;
|
|
});
|
|
}
|
|
loadCommands(vault) {
|
|
return __async(this, null, function* () {
|
|
if (!(yield vault.adapter.exists(LATEX_COMMANDS_PATH))) {
|
|
const defaultCommands = generateDefaultLatexCommands();
|
|
yield vault.adapter.write(LATEX_COMMANDS_PATH, JSON.stringify(defaultCommands, null, 2));
|
|
this.loadedCommands = defaultCommands;
|
|
} else {
|
|
const data = yield vault.adapter.read(LATEX_COMMANDS_PATH);
|
|
try {
|
|
const commands = JSON.parse(data);
|
|
const invalidCommand = commands.find((c) => getSuggestionDisplayName(c).includes("\n"));
|
|
if (invalidCommand)
|
|
throw new Error("Display name cannot contain a newline: " + getSuggestionDisplayName(invalidCommand));
|
|
this.loadedCommands = commands;
|
|
} catch (e) {
|
|
console.log("Completr latex commands parse error:", e.message);
|
|
new import_obsidian.Notice("Failed to parse latex commands file " + LATEX_COMMANDS_PATH + ". Using default commands.", 3e3);
|
|
this.loadedCommands = generateDefaultLatexCommands();
|
|
}
|
|
}
|
|
this.loadedCommands = SuggestionBlacklist.filter(this.loadedCommands);
|
|
});
|
|
}
|
|
};
|
|
var Latex = new LatexSuggestionProvider();
|
|
function generateEnvironments(environments) {
|
|
const result = [];
|
|
for (let i = 0; i < environments.length; i++) {
|
|
const environment = environments[i];
|
|
if (environment.hasStarVersion) {
|
|
environments.push(__spreadProps(__spreadValues({}, environment), { name: environment.name + "*", hasStarVersion: false }));
|
|
}
|
|
result.push({
|
|
displayName: `\\begin{${environment.name}}...`,
|
|
replacement: `\\begin{${environment.name}}${"{#}".repeat(environment.paramCount)}
|
|
${environment.paramCount < 1 ? "~\n" : ""}\\end{${environment.name}}`
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
function generateDefaultLatexCommands() {
|
|
return [
|
|
...generateEnvironments([
|
|
{ name: "align", paramCount: 0, hasStarVersion: true },
|
|
{ name: "alignat", paramCount: 1, hasStarVersion: true },
|
|
{ name: "aligned", paramCount: 0, hasStarVersion: false },
|
|
{ name: "alignedat", paramCount: 1, hasStarVersion: false },
|
|
{ name: "array", paramCount: 1, hasStarVersion: false },
|
|
{ name: "bmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "Bmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "bsmallmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "Bsmallmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "cases", paramCount: 0, hasStarVersion: true },
|
|
{ name: "crampedsubarray", paramCount: 1, hasStarVersion: false },
|
|
{ name: "dcases", paramCount: 0, hasStarVersion: true },
|
|
{ name: "drcases", paramCount: 0, hasStarVersion: true },
|
|
{ name: "empheq", paramCount: 2, hasStarVersion: false },
|
|
{ name: "eqnarray", paramCount: 0, hasStarVersion: true },
|
|
{ name: "equation", paramCount: 0, hasStarVersion: true },
|
|
{ name: "flalign", paramCount: 0, hasStarVersion: true },
|
|
{ name: "gather", paramCount: 0, hasStarVersion: true },
|
|
{ name: "gathered", paramCount: 0, hasStarVersion: false },
|
|
{ name: "lgathered", paramCount: 0, hasStarVersion: false },
|
|
{ name: "matrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "multiline", paramCount: 0, hasStarVersion: true },
|
|
{ name: "multilined", paramCount: 0, hasStarVersion: false },
|
|
{ name: "numcases", paramCount: 1, hasStarVersion: false },
|
|
{ name: "pmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "prooftree", paramCount: 0, hasStarVersion: false },
|
|
{ name: "psmallmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "rcases", paramCount: 0, hasStarVersion: true },
|
|
{ name: "rgathered", paramCount: 0, hasStarVersion: false },
|
|
{ name: "smallmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "split", paramCount: 0, hasStarVersion: false },
|
|
{ name: "spreadlines", paramCount: 1, hasStarVersion: false },
|
|
{ name: "subarray", paramCount: 1, hasStarVersion: false },
|
|
{ name: "subnumcases", paramCount: 1, hasStarVersion: false },
|
|
{ name: "vmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "Vmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "vsmallmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "Vsmallmatrix", paramCount: 0, hasStarVersion: true },
|
|
{ name: "xalignat", paramCount: 1, hasStarVersion: true },
|
|
{ name: "xxalignat", paramCount: 1, hasStarVersion: false }
|
|
]),
|
|
"\\above{#}{#}",
|
|
"\\verb|#|",
|
|
"\\left\\",
|
|
"\\right\\",
|
|
"\\acute{#}",
|
|
"\\aleph",
|
|
"\\alpha",
|
|
"\\amalg",
|
|
"\\And",
|
|
"\\angle",
|
|
"\\approx",
|
|
"\\approxeq",
|
|
"\\arccos",
|
|
"\\arcsin",
|
|
"\\arctan",
|
|
"\\arg",
|
|
"\\array{#}",
|
|
"\\arrowvert",
|
|
"\\Arrowvert",
|
|
"\\ast",
|
|
"\\asymp",
|
|
"\\atop",
|
|
"\\backepsilon",
|
|
"\\backprime",
|
|
"\\backsim",
|
|
"\\backsimeq",
|
|
"\\backslash",
|
|
"\\bar{#}",
|
|
"\\barwedge",
|
|
"\\Bbb{#}",
|
|
"\\Bbbk",
|
|
"\\bbFont",
|
|
"\\bbox{#}",
|
|
"\\bcancel{#}",
|
|
"\\because",
|
|
"\\beta",
|
|
"\\beth",
|
|
"\\between",
|
|
"\\bf",
|
|
"\\bigcap",
|
|
"\\bigcirc",
|
|
"\\bigcup",
|
|
"\\bigodot",
|
|
"\\bigoplus",
|
|
"\\bigotimes",
|
|
"\\bigsqcup",
|
|
"\\bigstar",
|
|
"\\bigtimes",
|
|
"\\bigtriangledown",
|
|
"\\bigtriangleup",
|
|
"\\biguplus",
|
|
"\\bigvee",
|
|
"\\bigwedge",
|
|
"\\binom{#}{#}",
|
|
"\\blacklozenge",
|
|
"\\blacksquare",
|
|
"\\blacktriangle",
|
|
"\\blacktriangledown",
|
|
"\\blacktriangleleft",
|
|
"\\blacktriangleright",
|
|
"\\bmod",
|
|
"\\boldsymbol{#}",
|
|
"\\bot",
|
|
"\\bowtie",
|
|
"\\Box",
|
|
"\\boxdot",
|
|
"\\boxed{#}",
|
|
"\\boxminus",
|
|
"\\boxplus",
|
|
"\\boxtimes",
|
|
"\\bra{#}",
|
|
"\\Bra{#}",
|
|
"\\brace",
|
|
"\\bracevert",
|
|
"\\brack",
|
|
"\\braket{#}",
|
|
"\\Braket{#}",
|
|
"\\breve{#}",
|
|
"\\bullet",
|
|
"\\bumpeq",
|
|
"\\Bumpeq",
|
|
"\\cal",
|
|
"\\cancel{#}",
|
|
"\\cancelto{#}{#}",
|
|
"\\cap",
|
|
"\\Cap",
|
|
"\\cases{#}",
|
|
"\\cdot",
|
|
"\\cdotp",
|
|
"\\cdots",
|
|
"\\celsius",
|
|
"\\centercolon",
|
|
"\\centerdot",
|
|
"\\centernot{#}",
|
|
"\\centerOver{#}{#}",
|
|
"\\cfrac{#}{#}",
|
|
"\\check{#}",
|
|
"\\checkmark",
|
|
"\\chi",
|
|
"\\choose",
|
|
"\\circ",
|
|
"\\circeq",
|
|
"\\circlearrowleft",
|
|
"\\circlearrowright",
|
|
"\\circledast",
|
|
"\\circledcirc",
|
|
"\\circleddash",
|
|
"\\circledR",
|
|
"\\circledS",
|
|
"\\clap{#}",
|
|
"\\class{#}{#}",
|
|
"\\clubsuit",
|
|
"\\colon",
|
|
"\\colonapprox",
|
|
"\\Colonapprox",
|
|
"\\coloneq",
|
|
"\\Coloneq",
|
|
"\\coloneqq",
|
|
"\\Coloneqq",
|
|
"\\colonsim",
|
|
"\\Colonsim",
|
|
"\\color{#}",
|
|
"\\colorbox{#}{#}",
|
|
"\\complement",
|
|
"\\cong",
|
|
"\\coprod",
|
|
"\\cos",
|
|
"\\cosh",
|
|
"\\cot",
|
|
"\\coth",
|
|
"\\cramped{#}",
|
|
"\\crampedclap{#}",
|
|
"\\crampedllap{#}",
|
|
"\\crampedrlap{#}",
|
|
"\\crampedsubstack{#}",
|
|
"\\csc",
|
|
"\\cssId{#}{#}",
|
|
"\\cup",
|
|
"\\Cup",
|
|
"\\curlyeqprec",
|
|
"\\curlyeqsucc",
|
|
"\\curlyvee",
|
|
"\\curlywedge",
|
|
"\\curvearrowleft",
|
|
"\\curvearrowright",
|
|
"\\dagger",
|
|
"\\daleth",
|
|
"\\dashleftarrow",
|
|
"\\dashrightarrow",
|
|
"\\dashv",
|
|
"\\dbinom{#}{#}",
|
|
"\\dblcolon",
|
|
"\\ddagger",
|
|
"\\ddddot{#}",
|
|
"\\dddot{#}",
|
|
"\\ddot{#}",
|
|
"\\ddots",
|
|
"\\DeclareMathOperator{#}{#}",
|
|
"\\DeclarePairedDelimiters{#}{#}{#}",
|
|
"\\DeclarePairedDelimitersX{#}{#}{#}{#}",
|
|
"\\DeclarePairedDelimitersXPP{#}{#}{#}{#}{#}{#}",
|
|
"\\deg",
|
|
"\\degree",
|
|
"\\delta",
|
|
"\\Delta",
|
|
"\\det",
|
|
"\\dfrac{#}{#}",
|
|
"\\diagdown",
|
|
"\\diagup",
|
|
"\\diamond",
|
|
"\\Diamond",
|
|
"\\diamondsuit",
|
|
"\\digamma",
|
|
"\\dim",
|
|
"\\displaylines{#}",
|
|
"\\displaystyle",
|
|
"\\div",
|
|
"\\divideontimes",
|
|
"\\divsymbol",
|
|
"\\dot{#}",
|
|
"\\doteq",
|
|
"\\Doteq",
|
|
"\\doteqdot",
|
|
"\\dotplus",
|
|
"\\dots",
|
|
"\\dotsb",
|
|
"\\dotsc",
|
|
"\\dotsi",
|
|
"\\dotsm",
|
|
"\\dotso",
|
|
"\\doublebarwedge",
|
|
"\\doublecap",
|
|
"\\doublecup",
|
|
"\\downarrow",
|
|
"\\Downarrow",
|
|
"\\downdownarrows",
|
|
"\\downharpoonleft",
|
|
"\\downharpoonright",
|
|
"\\ell",
|
|
"\\empheqbiglangle",
|
|
"\\empheqbiglbrace",
|
|
"\\empheqbiglbrack",
|
|
"\\empheqbiglceil",
|
|
"\\empheqbiglfloor",
|
|
"\\empheqbiglparen",
|
|
"\\empheqbiglvert",
|
|
"\\empheqbiglVert",
|
|
"\\empheqbigrangle",
|
|
"\\empheqbigrbrace",
|
|
"\\empheqbigrbrack",
|
|
"\\empheqbigrceil",
|
|
"\\empheqbigrfloor",
|
|
"\\empheqbigrparen",
|
|
"\\empheqbigrvert",
|
|
"\\empheqbigrVert",
|
|
"\\empheqlangle",
|
|
"\\empheqlbrace",
|
|
"\\empheqlbrack",
|
|
"\\empheqlceil",
|
|
"\\empheqlfloor",
|
|
"\\empheqlparen",
|
|
"\\empheqlvert",
|
|
"\\empheqlVert",
|
|
"\\empheqrangle",
|
|
"\\empheqrbrace",
|
|
"\\empheqrbrack",
|
|
"\\empheqrceil",
|
|
"\\empheqrfloor",
|
|
"\\empheqrparen",
|
|
"\\empheqrvert",
|
|
"\\empheqrVert",
|
|
"\\emptyset",
|
|
"\\enclose{#}{#}",
|
|
"\\enspace",
|
|
"\\epsilon",
|
|
"\\eqalign{#}",
|
|
"\\eqalignno{#}",
|
|
"\\eqcirc",
|
|
"\\eqcolon",
|
|
"\\Eqcolon",
|
|
"\\eqqcolon",
|
|
"\\Eqqcolon",
|
|
"\\eqref{#}",
|
|
"\\eqsim",
|
|
"\\eqslantgtr",
|
|
"\\eqslantless",
|
|
"\\equiv",
|
|
"\\eta",
|
|
"\\eth",
|
|
"\\exists",
|
|
"\\exp",
|
|
"\\fallingdotseq",
|
|
"\\fbox{#}",
|
|
"\\fCenter",
|
|
"\\fcolorbox{#}{#}{#}",
|
|
"\\Finv",
|
|
"\\flat",
|
|
"\\forall",
|
|
"\\frac{#}{#}",
|
|
"\\frak",
|
|
"\\framebox{#}",
|
|
"\\frown",
|
|
"\\Game",
|
|
"\\gamma",
|
|
"\\Gamma",
|
|
"\\gcd",
|
|
"\\ge",
|
|
"\\geq",
|
|
"\\geqq",
|
|
"\\geqslant",
|
|
"\\gets",
|
|
"\\gg",
|
|
"\\ggg",
|
|
"\\gggtr",
|
|
"\\gimel",
|
|
"\\gnapprox",
|
|
"\\gneq",
|
|
"\\gneqq",
|
|
"\\gnsim",
|
|
"\\grave{#}",
|
|
"\\gt",
|
|
"\\gtrapprox",
|
|
"\\gtrdot",
|
|
"\\gtreqless",
|
|
"\\gtreqqless",
|
|
"\\gtrless",
|
|
"\\gtrsim",
|
|
"\\gvertneqq",
|
|
"\\hat{#}",
|
|
"\\hbar",
|
|
"\\hbox{#}",
|
|
"\\heartsuit",
|
|
"\\hline",
|
|
"\\hom",
|
|
"\\hookleftarrow",
|
|
"\\hookrightarrow",
|
|
"\\hphantom{#}",
|
|
"\\href{#}{#}",
|
|
"\\hslash",
|
|
"\\huge",
|
|
"\\Huge",
|
|
"\\idotsint",
|
|
"\\iff",
|
|
"\\iiiint",
|
|
"\\iiint",
|
|
"\\iint",
|
|
"\\Im",
|
|
"\\imath",
|
|
"\\impliedby",
|
|
"\\implies",
|
|
"\\in",
|
|
"\\inf",
|
|
"\\infty",
|
|
"\\injlim",
|
|
"\\int",
|
|
"\\int^{#}_{#}",
|
|
"\\intercal",
|
|
"\\intop",
|
|
"\\iota",
|
|
"\\it",
|
|
"\\jmath",
|
|
"\\Join",
|
|
"\\kappa",
|
|
"\\ker",
|
|
"\\ket{#}",
|
|
"\\Ket{#}",
|
|
"\\ketbra{#}{#}",
|
|
"\\Ketbra{#}{#}",
|
|
"\\label{#}",
|
|
"\\lambda",
|
|
"\\Lambda",
|
|
"\\land",
|
|
"\\langle",
|
|
"\\large",
|
|
"\\Large",
|
|
"\\LARGE",
|
|
"\\LaTeX",
|
|
"\\lbrace",
|
|
"\\lbrack",
|
|
"\\lceil",
|
|
"\\ldots",
|
|
"\\ldotp",
|
|
"\\le",
|
|
"\\leadsto",
|
|
"\\Leftarrow",
|
|
"\\leftarrow",
|
|
"\\leftarrowtail",
|
|
"\\leftharpoondown",
|
|
"\\leftharpoonup",
|
|
"\\leftleftarrows",
|
|
"\\Leftrightarrow",
|
|
"\\leftrightarrow",
|
|
"\\leftrightarrows",
|
|
"\\leftrightharpoons",
|
|
"\\leftrightsquigarrow",
|
|
"\\leftthreetimes",
|
|
"\\leq",
|
|
"\\leqalignno{#}",
|
|
"\\leqq",
|
|
"\\leqslant",
|
|
"\\lessapprox",
|
|
"\\lessdot",
|
|
"\\lesseqgtr",
|
|
"\\lesseqqgtr",
|
|
"\\lessgtr",
|
|
"\\lesssim",
|
|
"\\lfloor",
|
|
"\\lg",
|
|
"\\lgroup",
|
|
"\\lhd",
|
|
"\\lim",
|
|
"\\lim_{#}",
|
|
"\\liminf",
|
|
"\\limsup",
|
|
"\\ll",
|
|
"\\llap{#}",
|
|
"\\llcorner",
|
|
"\\Lleftarrow",
|
|
"\\lll",
|
|
"\\llless",
|
|
"\\lmoustache",
|
|
"\\ln",
|
|
"\\lnapprox",
|
|
"\\lneq",
|
|
"\\lneqq",
|
|
"\\lnot",
|
|
"\\lnsim",
|
|
"\\log",
|
|
"\\longleftarrow",
|
|
"\\Longleftarrow",
|
|
"\\Longleftrightarrow",
|
|
"\\longleftrightarrow",
|
|
"\\longleftrightarrows",
|
|
"\\longLeftrightharpoons",
|
|
"\\longmapsto",
|
|
"\\longrightarrow",
|
|
"\\Longrightarrow",
|
|
"\\longrightleftharpoons",
|
|
"\\longRightleftharpoons",
|
|
"\\looparrowleft",
|
|
"\\looparrowright",
|
|
"\\lor",
|
|
"\\lozenge",
|
|
"\\lparen",
|
|
"\\lrcorner",
|
|
"\\Lsh",
|
|
"\\lt",
|
|
"\\ltimes",
|
|
"\\lvert",
|
|
"\\lVert",
|
|
"\\lvertneqq",
|
|
"\\maltese",
|
|
"\\mapsto",
|
|
"\\mathbb{#}",
|
|
"\\mathbb{R}",
|
|
"\\mathbb{N}",
|
|
"\\mathbb{C}",
|
|
"\\mathbb{Z}",
|
|
"\\mathbb{Q}",
|
|
"\\mathbf{#}",
|
|
"\\mathbfcal{#}",
|
|
"\\mathbffrak{#}",
|
|
"\\mathbfit{#}",
|
|
"\\mathbfscr{#}",
|
|
"\\mathbfsf{#}",
|
|
"\\mathbfsfit{#}",
|
|
"\\mathbfsfup{#}",
|
|
"\\mathbfup{#}",
|
|
"\\mathbin{#}",
|
|
"\\mathcal{#}",
|
|
"\\mathchoice{#}{#}{#}{#}",
|
|
"\\mathclap{#}",
|
|
"\\mathclose{#}",
|
|
"\\mathfrak{#}",
|
|
"\\mathinner{#}",
|
|
"\\mathit{#}",
|
|
"\\mathllap{#}",
|
|
"\\mathmakebox{#}",
|
|
"\\mathmbox{#}",
|
|
"\\mathnormal{#}",
|
|
"\\mathop{#}",
|
|
"\\mathopen{#}",
|
|
"\\mathord{#}",
|
|
"\\mathpunct{#}",
|
|
"\\mathrel{#}",
|
|
"\\mathring{#}",
|
|
"\\mathrlap{#}",
|
|
"\\mathrm{#}",
|
|
"\\mathscr{#}",
|
|
"\\mathsf{#}",
|
|
"\\mathsfit{#}",
|
|
"\\mathsfup{#}",
|
|
"\\mathstrut",
|
|
"\\mathtip{#}{#}",
|
|
"\\mathtt{#}",
|
|
"\\mathup{#}",
|
|
"\\max",
|
|
"\\mbox{#}",
|
|
"\\measuredangle",
|
|
"\\mho",
|
|
"\\micro",
|
|
"\\mid",
|
|
"\\min",
|
|
"\\mit",
|
|
"\\mod{#}",
|
|
"\\models",
|
|
"\\mp",
|
|
"\\MTThinColon",
|
|
"\\mu",
|
|
"\\multimap",
|
|
"\\nabla",
|
|
"\\natural",
|
|
"\\ncong",
|
|
"\\ndownarrow",
|
|
"\\ne",
|
|
"\\nearrow",
|
|
"\\neg",
|
|
"\\negmedspace",
|
|
"\\negthickspace",
|
|
"\\negthinspace",
|
|
"\\neq",
|
|
"\\newcommand{#}{#}",
|
|
"\\newenvironment{#}{#}{#}",
|
|
"\\newline",
|
|
"\\newtagform{#}{#}{#}",
|
|
"\\nexists",
|
|
"\\ngeq",
|
|
"\\ngeqq",
|
|
"\\ngeqslant",
|
|
"\\ngtr",
|
|
"\\ni",
|
|
"\\nleftarrow",
|
|
"\\nLeftarrow",
|
|
"\\nleftrightarrow",
|
|
"\\nLeftrightarrow",
|
|
"\\nleq",
|
|
"\\nleqq",
|
|
"\\nleqslant",
|
|
"\\nless",
|
|
"\\nmid",
|
|
"\\nobreakspace",
|
|
"\\nonscript",
|
|
"\\nonumber",
|
|
"\\normalsize",
|
|
"\\not",
|
|
"\\notag",
|
|
"\\notChar",
|
|
"\\notin",
|
|
"\\nparallel",
|
|
"\\nprec",
|
|
"\\npreceq",
|
|
"\\nrightarrow",
|
|
"\\nRightarrow",
|
|
"\\nshortmid",
|
|
"\\nshortparallel",
|
|
"\\nsim",
|
|
"\\nsubseteq",
|
|
"\\nsubseteqq",
|
|
"\\nsucc",
|
|
"\\nsucceq",
|
|
"\\nsupseteq",
|
|
"\\nsupseteqq",
|
|
"\\ntriangleleft",
|
|
"\\ntrianglelefteq",
|
|
"\\ntriangleright",
|
|
"\\ntrianglerighteq",
|
|
"\\nu",
|
|
"\\nuparrow",
|
|
"\\nvdash",
|
|
"\\nvDash",
|
|
"\\nVdash",
|
|
"\\nVDash",
|
|
"\\nwarrow",
|
|
"\\odot",
|
|
"\\ohm",
|
|
"\\oint",
|
|
"\\oldstyle",
|
|
"\\omega",
|
|
"\\Omega",
|
|
"\\omicron",
|
|
"\\ominus",
|
|
"\\operatorname{#}",
|
|
"\\oplus",
|
|
"\\ordinarycolon",
|
|
"\\oslash",
|
|
"\\otimes",
|
|
"\\over",
|
|
"\\overbrace{#}",
|
|
"\\overbracket{#}",
|
|
"\\overleftarrow{#}",
|
|
"\\overleftrightarrow{#}",
|
|
"\\overline{#}",
|
|
"\\overparen{#}",
|
|
"\\overrightarrow{#}",
|
|
"\\overset{#}{#}",
|
|
"\\overunderset{#}{#}{#}",
|
|
"\\owns",
|
|
"\\parallel",
|
|
"\\partial",
|
|
"\\perp",
|
|
"\\perthousand",
|
|
"\\phantom{#}",
|
|
"\\phi",
|
|
"\\Phi",
|
|
"\\pi",
|
|
"\\Pi",
|
|
"\\pitchfork",
|
|
"\\pm",
|
|
"\\pmb{#}",
|
|
"\\pmod{#}",
|
|
"\\pod{#}",
|
|
"\\Pr",
|
|
"\\prec",
|
|
"\\precapprox",
|
|
"\\preccurlyeq",
|
|
"\\preceq",
|
|
"\\precnapprox",
|
|
"\\precneqq",
|
|
"\\precnsim",
|
|
"\\precsim",
|
|
"\\prescript{#}{#}{#}",
|
|
"\\prime",
|
|
"\\prod",
|
|
"\\prod^{#}_{#}",
|
|
"\\projlim",
|
|
"\\propto",
|
|
"\\psi",
|
|
"\\Psi",
|
|
"\\qquad",
|
|
"\\quad",
|
|
"\\rangle",
|
|
"\\rbrace",
|
|
"\\rbrack",
|
|
"\\rceil",
|
|
"\\Re",
|
|
"\\ref{#}",
|
|
"\\refeq{#}",
|
|
"\\renewcommand{#}{#}",
|
|
"\\renewenvironment{#}{#}{#}",
|
|
"\\renewtagform{#}{#}{#}",
|
|
"\\restriction",
|
|
"\\rfloor",
|
|
"\\rgroup",
|
|
"\\rhd",
|
|
"\\rho",
|
|
"\\Rightarrow",
|
|
"\\rightarrow",
|
|
"\\rightarrowtail",
|
|
"\\rightharpoondown",
|
|
"\\rightharpoonup",
|
|
"\\rightleftarrows",
|
|
"\\rightleftharpoons",
|
|
"\\rightrightarrows",
|
|
"\\rightsquigarrow",
|
|
"\\rightthreetimes",
|
|
"\\risingdotseq",
|
|
"\\rlap{#}",
|
|
"\\rm",
|
|
"\\rmoustache",
|
|
"\\rparen",
|
|
"\\Rrightarrow",
|
|
"\\Rsh",
|
|
"\\rtimes",
|
|
"\\rvert",
|
|
"\\rVert",
|
|
"\\S",
|
|
"\\scr",
|
|
"\\scriptscriptstyle",
|
|
"\\scriptsize",
|
|
"\\scriptstyle",
|
|
"\\searrow",
|
|
"\\sec",
|
|
"\\set{#}",
|
|
"\\Set{#}",
|
|
"\\setminus",
|
|
"\\sf",
|
|
"\\sharp",
|
|
"\\shortmid",
|
|
"\\shortparallel",
|
|
"\\sideset{#}{#}{#}",
|
|
"\\sigma",
|
|
"\\Sigma",
|
|
"\\sim",
|
|
"\\simeq",
|
|
"\\sin",
|
|
"\\sinh",
|
|
"\\skew{#}{#}{#}",
|
|
"\\SkipLimits",
|
|
"\\small",
|
|
"\\smallfrown",
|
|
"\\smallint",
|
|
"\\smallsetminus",
|
|
"\\smallsmile",
|
|
"\\smash{#}",
|
|
"\\smile",
|
|
"\\space",
|
|
"\\spadesuit",
|
|
"\\sphericalangle",
|
|
"\\splitdfrac{#}{#}",
|
|
"\\splitfrac{#}{#}",
|
|
"\\sqcap",
|
|
"\\sqcup",
|
|
"\\sqrt{#}",
|
|
"\\sqsubset",
|
|
"\\sqsubseteq",
|
|
"\\sqsupset",
|
|
"\\sqsupseteq",
|
|
"\\square",
|
|
"\\stackbin{#}{#}",
|
|
"\\stackrel{#}{#}",
|
|
"\\star",
|
|
"\\strut",
|
|
"\\style{#}{#}",
|
|
"\\subset",
|
|
"\\Subset",
|
|
"\\subseteq",
|
|
"\\subseteqq",
|
|
"\\subsetneq",
|
|
"\\subsetneqq",
|
|
"\\substack{#}",
|
|
"\\succ",
|
|
"\\succapprox",
|
|
"\\succcurlyeq",
|
|
"\\succeq",
|
|
"\\succnapprox",
|
|
"\\succneqq",
|
|
"\\succnsim",
|
|
"\\succsim",
|
|
"\\sum",
|
|
"\\sum^{#}_{#}",
|
|
"\\sup",
|
|
"\\supset",
|
|
"\\Supset",
|
|
"\\supseteq",
|
|
"\\supseteqq",
|
|
"\\supsetneq",
|
|
"\\supsetneqq",
|
|
"\\surd",
|
|
"\\swarrow",
|
|
"\\symbb{#}",
|
|
"\\symbf{#}",
|
|
"\\symbfcal{#}",
|
|
"\\symbffrak{#}",
|
|
"\\symbfit{#}",
|
|
"\\symbfscr{#}",
|
|
"\\symbfsf{#}",
|
|
"\\symbfsfit{#}",
|
|
"\\symbfsfup{#}",
|
|
"\\symbfup{#}",
|
|
"\\symcal{#}",
|
|
"\\symfrak{#}",
|
|
"\\symit{#}",
|
|
"\\symnormal{#}",
|
|
"\\symrm{#}",
|
|
"\\symscr{#}",
|
|
"\\symsf{#}",
|
|
"\\symsfit{#}",
|
|
"\\symsfup{#}",
|
|
"\\symtt{#}",
|
|
"\\symup{#}",
|
|
"\\tag{#}",
|
|
"\\tan",
|
|
"\\tanh",
|
|
"\\tau",
|
|
"\\tbinom{#}{#}",
|
|
"\\TeX",
|
|
"\\text{#}",
|
|
"\\textacutedbl",
|
|
"\\textasciiacute",
|
|
"\\textasciibreve",
|
|
"\\textasciicaron",
|
|
"\\textasciicircum",
|
|
"\\textasciidieresis",
|
|
"\\textasciimacron",
|
|
"\\textasciitilde",
|
|
"\\textasteriskcentered",
|
|
"\\textbackslash",
|
|
"\\textbaht",
|
|
"\\textbar",
|
|
"\\textbardbl",
|
|
"\\textbf{#}",
|
|
"\\textbigcircle",
|
|
"\\textblank",
|
|
"\\textborn",
|
|
"\\textbraceleft",
|
|
"\\textbraceright",
|
|
"\\textbrokenbar",
|
|
"\\textbullet",
|
|
"\\textcelsius",
|
|
"\\textcent",
|
|
"\\textcentoldstyle",
|
|
"\\textcircledP",
|
|
"\\textclap{#}",
|
|
"\\textcolonmonetary",
|
|
"\\textcolor{#}{#}",
|
|
"\\textcompwordmark",
|
|
"\\textcopyleft",
|
|
"\\textcopyright",
|
|
"\\textcurrency",
|
|
"\\textdagger",
|
|
"\\textdaggerdbl",
|
|
"\\textdegree",
|
|
"\\textdied",
|
|
"\\textdiscount",
|
|
"\\textdiv",
|
|
"\\textdivorced",
|
|
"\\textdollar",
|
|
"\\textdollaroldstyle",
|
|
"\\textdong",
|
|
"\\textdownarrow",
|
|
"\\texteightoldstyle",
|
|
"\\textellipsis",
|
|
"\\textemdash",
|
|
"\\textendash",
|
|
"\\textestimated",
|
|
"\\texteuro",
|
|
"\\textexclamdown",
|
|
"\\textfiveoldstyle",
|
|
"\\textflorin",
|
|
"\\textfouroldstyle",
|
|
"\\textfractionsolidus",
|
|
"\\textgravedbl",
|
|
"\\textgreater",
|
|
"\\textguarani",
|
|
"\\textinterrobang",
|
|
"\\textinterrobangdown",
|
|
"\\textit{#}",
|
|
"\\textlangle",
|
|
"\\textlbrackdbl",
|
|
"\\textleftarrow",
|
|
"\\textless",
|
|
"\\textlira",
|
|
"\\textllap{#}",
|
|
"\\textlnot",
|
|
"\\textlquill",
|
|
"\\textmarried",
|
|
"\\textmho",
|
|
"\\textminus",
|
|
"\\textmu",
|
|
"\\textmusicalnote",
|
|
"\\textnaira",
|
|
"\\textnineoldstyle",
|
|
"\\textnormal{#}",
|
|
"\\textnumero",
|
|
"\\textohm",
|
|
"\\textonehalf",
|
|
"\\textoneoldstyle",
|
|
"\\textonequarter",
|
|
"\\textonesuperior",
|
|
"\\textopenbullet",
|
|
"\\textordfeminine",
|
|
"\\textordmasculine",
|
|
"\\textparagraph",
|
|
"\\textperiodcentered",
|
|
"\\textpertenthousand",
|
|
"\\textperthousand",
|
|
"\\textpeso",
|
|
"\\textpm",
|
|
"\\textquestiondown",
|
|
"\\textquotedblleft",
|
|
"\\textquotedblright",
|
|
"\\textquoteleft",
|
|
"\\textquoteright",
|
|
"\\textrangle",
|
|
"\\textrbrackdbl",
|
|
"\\textrecipe",
|
|
"\\textreferencemark",
|
|
"\\textregistered",
|
|
"\\textrightarrow",
|
|
"\\textrlap{#}",
|
|
"\\textrm{#}",
|
|
"\\textrquill",
|
|
"\\textsection",
|
|
"\\textservicemark",
|
|
"\\textsevenoldstyle",
|
|
"\\textsf{#}",
|
|
"\\textsixoldstyle",
|
|
"\\textsterling",
|
|
"\\textstyle",
|
|
"\\textsurd",
|
|
"\\textthreeoldstyle",
|
|
"\\textthreequarters",
|
|
"\\textthreesuperior",
|
|
"\\texttildelow",
|
|
"\\texttimes",
|
|
"\\texttip{#}{#}",
|
|
"\\texttrademark",
|
|
"\\texttt{#}",
|
|
"\\texttwooldstyle",
|
|
"\\texttwosuperior",
|
|
"\\textunderscore",
|
|
"\\textup{#}",
|
|
"\\textuparrow",
|
|
"\\textvisiblespace",
|
|
"\\textwon",
|
|
"\\textyen",
|
|
"\\textzerooldstyle",
|
|
"\\tfrac{#}{#}",
|
|
"\\therefore",
|
|
"\\theta",
|
|
"\\Theta",
|
|
"\\thickapprox",
|
|
"\\thicksim",
|
|
"\\thinspace",
|
|
"\\tilde{#}",
|
|
"\\times",
|
|
"\\tiny",
|
|
"\\Tiny",
|
|
"\\to",
|
|
"\\top",
|
|
"\\triangle",
|
|
"\\triangledown",
|
|
"\\triangleleft",
|
|
"\\trianglelefteq",
|
|
"\\triangleq",
|
|
"\\triangleright",
|
|
"\\trianglerighteq",
|
|
"\\tripledash",
|
|
"\\tt",
|
|
"\\twoheadleftarrow",
|
|
"\\twoheadrightarrow",
|
|
"\\ulcorner",
|
|
"\\underbrace{#}",
|
|
"\\underbracket{#}",
|
|
"\\underleftarrow{#}",
|
|
"\\underleftrightarrow{#}",
|
|
"\\underline{#}",
|
|
"\\underparen{#}",
|
|
"\\underrightarrow{#}",
|
|
"\\underset{#}{#}",
|
|
"\\unicode{#}",
|
|
"\\unlhd",
|
|
"\\unrhd",
|
|
"\\upalpha",
|
|
"\\uparrow",
|
|
"\\Uparrow",
|
|
"\\upbeta",
|
|
"\\upchi",
|
|
"\\updelta",
|
|
"\\Updelta",
|
|
"\\updownarrow",
|
|
"\\Updownarrow",
|
|
"\\upepsilon",
|
|
"\\upeta",
|
|
"\\upgamma",
|
|
"\\Upgamma",
|
|
"\\upharpoonleft",
|
|
"\\upharpoonright",
|
|
"\\upiota",
|
|
"\\upkappa",
|
|
"\\uplambda",
|
|
"\\Uplambda",
|
|
"\\uplus",
|
|
"\\upmu",
|
|
"\\upnu",
|
|
"\\upomega",
|
|
"\\Upomega",
|
|
"\\upomicron",
|
|
"\\upphi",
|
|
"\\Upphi",
|
|
"\\uppi",
|
|
"\\Uppi",
|
|
"\\uppsi",
|
|
"\\Uppsi",
|
|
"\\uprho",
|
|
"\\upsigma",
|
|
"\\Upsigma",
|
|
"\\upsilon",
|
|
"\\Upsilon",
|
|
"\\uptau",
|
|
"\\uptheta",
|
|
"\\Uptheta",
|
|
"\\upuparrows",
|
|
"\\upupsilon",
|
|
"\\Upupsilon",
|
|
"\\upvarepsilon",
|
|
"\\upvarphi",
|
|
"\\upvarpi",
|
|
"\\upvarrho",
|
|
"\\upvarsigma",
|
|
"\\upvartheta",
|
|
"\\upxi",
|
|
"\\Upxi",
|
|
"\\upzeta",
|
|
"\\urcorner",
|
|
"\\usetagform{#}",
|
|
"\\varDelta",
|
|
"\\varepsilon",
|
|
"\\varGamma",
|
|
"\\varinjlim",
|
|
"\\varkappa",
|
|
"\\varLambda",
|
|
"\\varliminf",
|
|
"\\varlimsup",
|
|
"\\varnothing",
|
|
"\\varOmega",
|
|
"\\varphi",
|
|
"\\varPhi",
|
|
"\\varpi",
|
|
"\\varPi",
|
|
"\\varprojlim",
|
|
"\\varpropto",
|
|
"\\varPsi",
|
|
"\\varrho",
|
|
"\\varsigma",
|
|
"\\varSigma",
|
|
"\\varsubsetneq",
|
|
"\\varsubsetneqq",
|
|
"\\varsupsetneq",
|
|
"\\varsupsetneqq",
|
|
"\\vartheta",
|
|
"\\varTheta",
|
|
"\\vartriangle",
|
|
"\\vartriangleleft",
|
|
"\\vartriangleright",
|
|
"\\varUpsilon",
|
|
"\\varXi",
|
|
"\\vcenter{#}",
|
|
"\\vdash",
|
|
"\\vDash",
|
|
"\\Vdash",
|
|
"\\vdots",
|
|
"\\vec{#}",
|
|
"\\vee",
|
|
"\\veebar",
|
|
"\\Vert",
|
|
"\\vert",
|
|
"\\vphantom{#}",
|
|
"\\Vvdash",
|
|
"\\wedge",
|
|
"\\widehat{#}",
|
|
"\\widetilde{#}",
|
|
"\\wp",
|
|
"\\wr",
|
|
"\\xcancel{#}",
|
|
"\\xhookleftarrow{#}",
|
|
"\\xhookrightarrow{#}",
|
|
"\\xi",
|
|
"\\Xi",
|
|
"\\xleftarrow{#}",
|
|
"\\xLeftarrow{#}",
|
|
"\\xleftharpoondown{#}",
|
|
"\\xleftharpoonup{#}",
|
|
"\\xleftrightarrow{#}",
|
|
"\\xLeftrightarrow{#}",
|
|
"\\xleftrightharpoons{#}",
|
|
"\\xLeftrightharpoons{#}",
|
|
"\\xlongequal{#}",
|
|
"\\xmapsto{#}",
|
|
"\\xmathstrut{#}",
|
|
"\\xrightarrow{#}",
|
|
"\\xRightarrow{#}",
|
|
"\\xrightharpoondown{#}",
|
|
"\\xrightharpoonup{#}",
|
|
"\\xrightleftharpoons{#}",
|
|
"\\xRightleftharpoons{#}",
|
|
"\\xtofrom{#}",
|
|
"\\xtwoheadleftarrow{#}",
|
|
"\\xtwoheadrightarrow{#}",
|
|
"\\yen",
|
|
"\\zeta"
|
|
];
|
|
}
|
|
|
|
// src/settings.ts
|
|
var DEFAULT_SETTINGS = {
|
|
characterRegex: "a-zA-Z\xF6\xE4\xFC\xD6\xC4\xDC\xDF",
|
|
maxLookBackDistance: 50,
|
|
minWordLength: 2,
|
|
minWordTriggerLength: 3,
|
|
wordInsertionMode: "Ignore-Case & Replace" /* IGNORE_CASE_REPLACE */,
|
|
ignoreDiacriticsWhenFiltering: false,
|
|
latexProviderEnabled: true,
|
|
latexTriggerInCodeBlocks: true,
|
|
latexMinWordTriggerLength: 2,
|
|
latexIgnoreCase: false,
|
|
fileScannerProviderEnabled: true,
|
|
fileScannerScanCurrent: true,
|
|
wordListProviderEnabled: true,
|
|
frontMatterProviderEnabled: true,
|
|
frontMatterTagAppendSuffix: true,
|
|
frontMatterIgnoreCase: true
|
|
};
|
|
|
|
// src/provider/dictionary_provider.ts
|
|
var DictionaryProvider = class {
|
|
getSuggestions(context, settings) {
|
|
var _a, _b, _c;
|
|
if (!this.isEnabled(settings) || !context.query || context.query.length < settings.minWordTriggerLength)
|
|
return [];
|
|
const ignoreCase = settings.wordInsertionMode != "Match-Case & Replace" /* MATCH_CASE_REPLACE */;
|
|
let query = maybeLowerCase(context.query, ignoreCase);
|
|
const ignoreDiacritics = settings.ignoreDiacriticsWhenFiltering;
|
|
if (ignoreDiacritics)
|
|
query = removeDiacritics(query);
|
|
const firstChar = query.charAt(0);
|
|
const list = ignoreCase ? [(_a = this.wordMap.get(firstChar)) != null ? _a : [], (_b = this.wordMap.get(firstChar.toUpperCase())) != null ? _b : []] : [(_c = this.wordMap.get(firstChar)) != null ? _c : []];
|
|
if (ignoreDiacritics) {
|
|
for (let [key, value] of this.wordMap.entries()) {
|
|
let keyFirstChar = maybeLowerCase(key.charAt(0), ignoreCase);
|
|
if (removeDiacritics(keyFirstChar) === firstChar)
|
|
list.push(value);
|
|
}
|
|
}
|
|
if (!list || list.length < 1)
|
|
return [];
|
|
const result = /* @__PURE__ */ new Set();
|
|
for (let el of list) {
|
|
filterMapIntoSet(
|
|
result,
|
|
el,
|
|
(s) => {
|
|
let match = maybeLowerCase(s, ignoreCase);
|
|
if (ignoreDiacritics)
|
|
match = removeDiacritics(match);
|
|
return match.startsWith(query);
|
|
},
|
|
settings.wordInsertionMode === "Ignore-Case & Append" /* IGNORE_CASE_APPEND */ ? (s) => context.query + s.substring(query.length, s.length) : (s) => s
|
|
);
|
|
}
|
|
return [...result].sort((a, b) => a.length - b.length);
|
|
}
|
|
};
|
|
var DIACRITICS_REGEX = /[\u0300-\u036f]/g;
|
|
function removeDiacritics(str) {
|
|
return str.normalize("NFD").replace(DIACRITICS_REGEX, "");
|
|
}
|
|
function filterMapIntoSet(set, iterable, predicate, map) {
|
|
for (let val of iterable) {
|
|
if (!predicate(val))
|
|
continue;
|
|
set.add(map(val));
|
|
}
|
|
}
|
|
|
|
// src/provider/word_list_provider.ts
|
|
var BASE_FOLDER_PATH = ".obsidian/plugins/obsidian-completr/wordLists";
|
|
var NEW_LINE_REGEX2 = /\r?\n/;
|
|
var WordListSuggestionProvider = class extends DictionaryProvider {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.wordMap = /* @__PURE__ */ new Map();
|
|
}
|
|
isEnabled(settings) {
|
|
return settings.wordListProviderEnabled;
|
|
}
|
|
loadFromFiles(vault, settings) {
|
|
return __async(this, null, function* () {
|
|
this.wordMap.clear();
|
|
const fileNames = yield this.getRelativeFilePaths(vault);
|
|
for (let i = fileNames.length - 1; i >= 0; i--) {
|
|
const fileName = fileNames[i];
|
|
let data;
|
|
try {
|
|
data = yield vault.adapter.read(fileName);
|
|
} catch (e) {
|
|
console.log("Completr: Unable to read " + fileName);
|
|
continue;
|
|
}
|
|
const lines = data.split(NEW_LINE_REGEX2);
|
|
for (let line of lines) {
|
|
if (line === "" || line.length < settings.minWordLength)
|
|
continue;
|
|
let list = this.wordMap.get(line.charAt(0));
|
|
if (!list) {
|
|
list = [];
|
|
this.wordMap.set(line.charAt(0), list);
|
|
}
|
|
list.push(line.trim());
|
|
}
|
|
}
|
|
let count = 0;
|
|
for (let entry of this.wordMap.entries()) {
|
|
const newValue = SuggestionBlacklist.filter(entry[1].sort((a, b) => a.length - b.length));
|
|
this.wordMap.set(entry[0], newValue);
|
|
count += newValue.length;
|
|
}
|
|
return count;
|
|
});
|
|
}
|
|
deleteWordList(vault, path) {
|
|
return __async(this, null, function* () {
|
|
yield vault.adapter.remove(path);
|
|
});
|
|
}
|
|
importWordList(vault, name, text) {
|
|
return __async(this, null, function* () {
|
|
const path = BASE_FOLDER_PATH + "/" + name;
|
|
if (yield vault.adapter.exists(path))
|
|
return false;
|
|
yield vault.adapter.write(path, text);
|
|
return true;
|
|
});
|
|
}
|
|
getRelativeFilePaths(vault) {
|
|
return __async(this, null, function* () {
|
|
if (!(yield vault.adapter.exists(BASE_FOLDER_PATH)))
|
|
yield vault.adapter.mkdir(BASE_FOLDER_PATH);
|
|
return (yield vault.adapter.list(BASE_FOLDER_PATH)).files;
|
|
});
|
|
}
|
|
};
|
|
var WordList = new WordListSuggestionProvider();
|
|
|
|
// src/provider/scanner_provider.ts
|
|
var SCANNED_WORDS_PATH = ".obsidian/plugins/obsidian-completr/scanned_words.txt";
|
|
var NEW_LINE_REGEX3 = /\r?\n/;
|
|
var ScannerSuggestionProvider = class extends DictionaryProvider {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.wordMap = /* @__PURE__ */ new Map();
|
|
}
|
|
isEnabled(settings) {
|
|
return settings.fileScannerProviderEnabled;
|
|
}
|
|
scanFiles(settings, files) {
|
|
return __async(this, null, function* () {
|
|
for (let file of files) {
|
|
yield this.scanFile(settings, file, false);
|
|
}
|
|
yield this.saveData(files[0].vault);
|
|
});
|
|
}
|
|
scanFile(settings, file, saveImmediately) {
|
|
return __async(this, null, function* () {
|
|
const contents = yield file.vault.cachedRead(file);
|
|
const regex = new RegExp("\\$+.*?\\$+|`+.*?`+|\\[+.*?\\]+|([" + settings.characterRegex + "]+)", "gsu");
|
|
for (let match of contents.matchAll(regex)) {
|
|
const groupValue = match[1];
|
|
if (!groupValue || groupValue.length < settings.minWordLength)
|
|
continue;
|
|
this.addWord(groupValue);
|
|
}
|
|
if (saveImmediately)
|
|
yield this.saveData(file.vault);
|
|
});
|
|
}
|
|
saveData(vault) {
|
|
return __async(this, null, function* () {
|
|
let output = [];
|
|
for (let entry of this.wordMap.entries()) {
|
|
output = [...output, ...entry[1]];
|
|
}
|
|
yield vault.adapter.write(SCANNED_WORDS_PATH, output.join("\n"));
|
|
});
|
|
}
|
|
loadData(vault) {
|
|
return __async(this, null, function* () {
|
|
if (!(yield vault.adapter.exists(SCANNED_WORDS_PATH)))
|
|
return;
|
|
const contents = (yield vault.adapter.read(SCANNED_WORDS_PATH)).split(NEW_LINE_REGEX3);
|
|
for (let word of contents) {
|
|
this.addWord(word);
|
|
}
|
|
});
|
|
}
|
|
deleteAllWords(vault) {
|
|
return __async(this, null, function* () {
|
|
this.wordMap.clear();
|
|
yield this.saveData(vault);
|
|
});
|
|
}
|
|
addWord(word) {
|
|
if (!word || SuggestionBlacklist.has(word))
|
|
return;
|
|
let list = this.wordMap.get(word.charAt(0));
|
|
if (!list) {
|
|
list = /* @__PURE__ */ new Set();
|
|
this.wordMap.set(word.charAt(0), list);
|
|
}
|
|
list.add(word);
|
|
}
|
|
};
|
|
var FileScanner = new ScannerSuggestionProvider();
|
|
|
|
// src/popup.ts
|
|
var import_obsidian3 = require("obsidian");
|
|
|
|
// src/provider/front_matter_provider.ts
|
|
var import_obsidian2 = require("obsidian");
|
|
var BASE_SUGGESTION = {
|
|
displayName: "front-matter",
|
|
replacement: "---\n~\n---",
|
|
overrideStart: { line: 0, ch: 0 }
|
|
};
|
|
var PUBLISH_SUGGESTION = {
|
|
displayName: "publish: #",
|
|
replacement: "publish: ~"
|
|
};
|
|
function findTagCompletionType(keyInfo, editor, currentLineIndex, currentLine, ignoreCase) {
|
|
const key = maybeLowerCase(keyInfo.key, ignoreCase);
|
|
const isList = keyInfo.isList;
|
|
if (currentLine.startsWith(key + ": "))
|
|
return "inline";
|
|
if (!currentLine.startsWith("- ") || !isList)
|
|
return "none";
|
|
let foundListStart = false;
|
|
for (let i = currentLineIndex - 1; i >= 1; i--) {
|
|
let line = editor.getLine(i).trim();
|
|
if (line.endsWith(":")) {
|
|
foundListStart = line.startsWith(key + ":");
|
|
break;
|
|
}
|
|
}
|
|
return foundListStart ? "multiline" : "none";
|
|
}
|
|
var YAMLKeyInfo = class {
|
|
constructor(key) {
|
|
this.key = key;
|
|
this.completions = /* @__PURE__ */ new Set();
|
|
}
|
|
addCompletion(value) {
|
|
this.completions.add(value);
|
|
}
|
|
};
|
|
var YAMLKeyCache = class {
|
|
constructor() {
|
|
this.keyMap = /* @__PURE__ */ new Map();
|
|
}
|
|
addEntry(key, value) {
|
|
let info = this.keyMap.get(key);
|
|
if (!info)
|
|
this.keyMap.set(key, info = new YAMLKeyInfo(key));
|
|
info.addCompletion(value);
|
|
}
|
|
addEntries(key, values) {
|
|
let info = this.keyMap.get(key);
|
|
if (!info)
|
|
this.keyMap.set(key, info = new YAMLKeyInfo(key));
|
|
for (let value of values) {
|
|
if (!value)
|
|
continue;
|
|
info.addCompletion(value);
|
|
}
|
|
info.isList = true;
|
|
}
|
|
getCompletions() {
|
|
return this.keyMap.values();
|
|
}
|
|
};
|
|
var FrontMatterSuggestionProvider = class {
|
|
constructor() {
|
|
this.blocksAllOtherProviders = true;
|
|
this.fileSuggestionCache = /* @__PURE__ */ new Map();
|
|
this.onCacheChange = (file, data, cache) => {
|
|
this.addKeyCompletionsFromFile(file, cache);
|
|
};
|
|
}
|
|
getSuggestions(context, settings) {
|
|
var _a;
|
|
if (!settings.frontMatterProviderEnabled)
|
|
return [];
|
|
const firstLine = context.editor.getLine(0);
|
|
const isInFrontMatter = isInFrontMatterBlock(context.editor, context.start);
|
|
const ignoreCase = settings.frontMatterIgnoreCase;
|
|
if (!isInFrontMatter && context.start.line === 0 && (firstLine === "" || "front-matter".startsWith(maybeLowerCase(firstLine, ignoreCase)))) {
|
|
return [BASE_SUGGESTION];
|
|
} else if (!isInFrontMatter) {
|
|
return [];
|
|
}
|
|
const query = maybeLowerCase(context.query, ignoreCase);
|
|
if (context.start.ch === 0) {
|
|
const suggestions = this.getPossibleCompletions().flatMap((i) => {
|
|
if (!i.isList) {
|
|
return [{
|
|
displayName: i.key + ": #",
|
|
replacement: i.key + ": ~"
|
|
}];
|
|
}
|
|
return [
|
|
{
|
|
displayName: i.key + ": [#]",
|
|
replacement: i.key + ": [~]"
|
|
},
|
|
{
|
|
displayName: i.key + ": \\...",
|
|
replacement: i.key + ":\n- ~"
|
|
}
|
|
];
|
|
});
|
|
suggestions.push(PUBLISH_SUGGESTION);
|
|
return suggestions.filter((snippet) => {
|
|
const displayName = getSuggestionDisplayName(snippet, ignoreCase);
|
|
const key2 = displayName.substring(0, displayName.indexOf(":"));
|
|
return key2.startsWith(query);
|
|
});
|
|
}
|
|
const currentLine = maybeLowerCase(context.editor.getLine(context.start.line), ignoreCase);
|
|
if (currentLine.startsWith("publish:"))
|
|
return FrontMatterSuggestionProvider.getPublishSuggestions(query);
|
|
const { key, type } = (_a = this.getPossibleCompletions().map((possibleKey) => ({
|
|
key: possibleKey,
|
|
type: findTagCompletionType(possibleKey, context.editor, context.start.line, currentLine, ignoreCase)
|
|
})).filter(({ type: type2 }) => type2 !== "none").shift()) != null ? _a : {};
|
|
if (!key)
|
|
return [];
|
|
const customQuery = maybeLowerCase(matchWordBackwards(
|
|
context.editor,
|
|
context.end,
|
|
(char) => new RegExp("[" + settings.characterRegex + "/\\-_]", "u").test(char),
|
|
settings.maxLookBackDistance
|
|
).query, ignoreCase);
|
|
return [...key.completions].filter((tag) => maybeLowerCase(tag, ignoreCase).startsWith(customQuery)).map((tag) => ({
|
|
displayName: tag,
|
|
replacement: tag + (settings.frontMatterTagAppendSuffix && key.isList ? type === "inline" ? ", " : "\n- " : ""),
|
|
overrideStart: __spreadProps(__spreadValues({}, context.end), { ch: context.end.ch - customQuery.length })
|
|
})).sort((a, b) => a.displayName.length - b.displayName.length);
|
|
}
|
|
loadYAMLKeyCompletions(cache, files) {
|
|
for (let file of files) {
|
|
this.addKeyCompletionsFromFile(file, cache.getFileCache(file));
|
|
}
|
|
}
|
|
addKeyCompletionsFromFile(file, cache) {
|
|
if (!file || !cache || !cache.frontmatter) {
|
|
return;
|
|
}
|
|
const keyCache = new YAMLKeyCache();
|
|
this.fileSuggestionCache.set(file.path, keyCache);
|
|
for (let key of Object.keys(cache.frontmatter)) {
|
|
if (key === "position" || key === "publish" || key === "tags")
|
|
continue;
|
|
let prop = cache.frontmatter[key];
|
|
if (!prop)
|
|
continue;
|
|
if (Array.isArray(prop)) {
|
|
keyCache.addEntries(key, prop);
|
|
} else {
|
|
keyCache.addEntry(key, prop);
|
|
}
|
|
}
|
|
const tags = (0, import_obsidian2.getAllTags)(cache);
|
|
if (tags && tags.length > 0)
|
|
keyCache.addEntries("tags", tags.map((t) => t.substring(1)));
|
|
}
|
|
getPossibleCompletions() {
|
|
const allKeys = /* @__PURE__ */ new Map();
|
|
for (let cache of this.fileSuggestionCache.values()) {
|
|
for (let keyInfo of cache.getCompletions()) {
|
|
let combinedKeyInfo = allKeys.get(keyInfo.key);
|
|
if (!combinedKeyInfo)
|
|
allKeys.set(keyInfo.key, combinedKeyInfo = new YAMLKeyInfo(keyInfo.key));
|
|
keyInfo.completions.forEach((c) => combinedKeyInfo.addCompletion(c));
|
|
combinedKeyInfo.isList = combinedKeyInfo.isList || keyInfo.isList;
|
|
}
|
|
}
|
|
return [...allKeys.values()];
|
|
}
|
|
static getPublishSuggestions(query) {
|
|
const possibilities = ["true", "false"];
|
|
const partialMatches = possibilities.filter((val) => val.startsWith(query) && val !== query);
|
|
if (partialMatches.length > 0)
|
|
return partialMatches;
|
|
else if (query === "true" || query === "false")
|
|
return query === "true" ? possibilities.reverse() : possibilities;
|
|
return [];
|
|
}
|
|
};
|
|
var FrontMatter = new FrontMatterSuggestionProvider();
|
|
|
|
// src/popup.ts
|
|
var PROVIDERS = [FrontMatter, Latex, FileScanner, WordList];
|
|
var SuggestionPopup = class extends import_obsidian3.EditorSuggest {
|
|
constructor(app, settings, snippetManager) {
|
|
var _a;
|
|
super(app);
|
|
this.disableSnippets = (_a = app.vault.config) == null ? void 0 : _a.legacyEditor;
|
|
this.settings = settings;
|
|
this.snippetManager = snippetManager;
|
|
let self = this;
|
|
self.scope.keys = [];
|
|
}
|
|
getSuggestions(context) {
|
|
let suggestions = [];
|
|
for (let provider of PROVIDERS) {
|
|
suggestions = [...suggestions, ...provider.getSuggestions(__spreadProps(__spreadValues({}, context), {
|
|
separatorChar: this.separatorChar
|
|
}), this.settings)];
|
|
if (provider.blocksAllOtherProviders && suggestions.length > 0) {
|
|
suggestions.forEach((suggestion) => {
|
|
if (typeof suggestion === "string" || !suggestion.overrideStart)
|
|
return;
|
|
this.context.start = suggestion.overrideStart;
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
return suggestions.length === 0 ? null : suggestions.filter((s) => !SuggestionBlacklist.has(s));
|
|
}
|
|
onTrigger(cursor, editor, file) {
|
|
if (this.justClosed) {
|
|
this.justClosed = false;
|
|
return null;
|
|
}
|
|
let {
|
|
query,
|
|
separatorChar
|
|
} = matchWordBackwards(editor, cursor, (char) => this.getCharacterRegex().test(char), this.settings.maxLookBackDistance);
|
|
this.separatorChar = separatorChar;
|
|
return {
|
|
start: __spreadProps(__spreadValues({}, cursor), {
|
|
ch: cursor.ch - query.length
|
|
}),
|
|
end: cursor,
|
|
query
|
|
};
|
|
}
|
|
renderSuggestion(value, el) {
|
|
el.addClass("completr-suggestion-item");
|
|
el.setText(getSuggestionDisplayName(value));
|
|
}
|
|
selectSuggestion(value, evt) {
|
|
const replacement = getSuggestionReplacement(value);
|
|
const start = typeof value !== "string" && value.overrideStart ? value.overrideStart : this.context.start;
|
|
const endPos = this.context.end;
|
|
this.context.editor.replaceRange(replacement, start, __spreadProps(__spreadValues({}, endPos), {
|
|
ch: Math.min(endPos.ch, this.context.editor.getLine(endPos.line).length)
|
|
}));
|
|
if (replacement.contains("#") || replacement.contains("~")) {
|
|
if (!this.disableSnippets) {
|
|
this.snippetManager.handleSnippet(replacement, start, this.context.editor);
|
|
} else {
|
|
console.log("Completr: Please enable Live Preview mode to use snippets");
|
|
}
|
|
} else {
|
|
this.context.editor.setCursor(__spreadProps(__spreadValues({}, start), { ch: start.ch + replacement.length }));
|
|
}
|
|
this.close();
|
|
this.justClosed = true;
|
|
}
|
|
selectNextItem(dir) {
|
|
const self = this;
|
|
self.suggestions.setSelectedItem(self.suggestions.selectedItem + dir, new KeyboardEvent("keydown"));
|
|
}
|
|
getSelectedItem() {
|
|
const self = this;
|
|
return self.suggestions.values[self.suggestions.selectedItem];
|
|
}
|
|
applySelectedItem() {
|
|
const self = this;
|
|
self.suggestions.useSelectedItem();
|
|
}
|
|
isVisible() {
|
|
return this.isOpen;
|
|
}
|
|
preventNextTrigger() {
|
|
this.justClosed = true;
|
|
}
|
|
getCharacterRegex() {
|
|
if (this.characterRegex !== this.settings.characterRegex)
|
|
this.compiledCharacterRegex = new RegExp("[" + this.settings.characterRegex + "]", "u");
|
|
return this.compiledCharacterRegex;
|
|
}
|
|
};
|
|
|
|
// src/settings_tab.ts
|
|
var import_obsidian4 = require("obsidian");
|
|
var CompletrSettingsTab = class extends import_obsidian4.PluginSettingTab {
|
|
constructor(app, plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
display() {
|
|
const { containerEl } = this;
|
|
containerEl.empty();
|
|
new import_obsidian4.Setting(containerEl).setName("Word character regex").setDesc("A regular expression which matches a character of a word. Used by during completion to find the word to the left of the cursor and used by the file scanner to find valid words.").addText((text) => text.setValue(this.plugin.settings.characterRegex).onChange((val) => __async(this, null, function* () {
|
|
try {
|
|
new RegExp("[" + val + "]+").test("");
|
|
text.inputEl.removeClass("completr-settings-error");
|
|
this.plugin.settings.characterRegex = val;
|
|
yield this.plugin.saveSettings();
|
|
} catch (e) {
|
|
text.inputEl.addClass("completr-settings-error");
|
|
}
|
|
})));
|
|
new import_obsidian4.Setting(containerEl).setName("Minimum word length").setDesc("The minimum length a word has to be, to count as a valid suggestion. This value is used by the file scanner and word list provider.").addText((text) => {
|
|
text.inputEl.type = "number";
|
|
text.setValue(this.plugin.settings.minWordLength + "").onChange((val) => __async(this, null, function* () {
|
|
if (!val || val.length < 1)
|
|
return;
|
|
this.plugin.settings.minWordLength = parseInt(val);
|
|
yield this.plugin.saveSettings();
|
|
}));
|
|
});
|
|
new import_obsidian4.Setting(containerEl).setName("Minimum word trigger length").setDesc("The minimum length a word has to be, to trigger suggestions. The LaTeX provider has its own separate setting.").addText((text) => {
|
|
text.inputEl.type = "number";
|
|
text.setValue(this.plugin.settings.minWordTriggerLength + "").onChange((val) => __async(this, null, function* () {
|
|
if (!val || val.length < 1)
|
|
return;
|
|
this.plugin.settings.minWordTriggerLength = parseInt(val);
|
|
yield this.plugin.saveSettings();
|
|
}));
|
|
});
|
|
new import_obsidian4.Setting(containerEl).setName("Word insertion mode").setDesc("The insertion mode that is used. Ignore-case would suggest 'Hello' if the typed text is 'hello', match-case would not. Append would complete 'Hell' with 'Hello' while replace would complete it with 'hello' instead (if only 'hello' was a known word). Only used by the file scanner and word list provider.").addDropdown(
|
|
(dropdown) => dropdown.addOption("Ignore-Case & Replace" /* IGNORE_CASE_REPLACE */, "Ignore-Case & Replace" /* IGNORE_CASE_REPLACE */).addOption("Ignore-Case & Append" /* IGNORE_CASE_APPEND */, "Ignore-Case & Append" /* IGNORE_CASE_APPEND */).addOption("Match-Case & Replace" /* MATCH_CASE_REPLACE */, "Match-Case & Replace" /* MATCH_CASE_REPLACE */).setValue(this.plugin.settings.wordInsertionMode).onChange((val) => __async(this, null, function* () {
|
|
this.plugin.settings.wordInsertionMode = val;
|
|
yield this.plugin.saveSettings();
|
|
}))
|
|
);
|
|
new import_obsidian4.Setting(containerEl).setName("Ignore diacritics when filtering").setDesc("When enabled, the query 'Hello' can suggest 'H\xE8ll\xF2', meaning diacritics will be ignored when filtering the suggestions. Only used by the file scanner and word list provider.").addToggle((toggle) => toggle.setValue(this.plugin.settings.ignoreDiacriticsWhenFiltering).onChange((val) => __async(this, null, function* () {
|
|
this.plugin.settings.ignoreDiacriticsWhenFiltering = val;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new import_obsidian4.Setting(containerEl).setName("Latex provider").setHeading();
|
|
this.createEnabledSetting("latexProviderEnabled", "Whether or not the latex provider is enabled", containerEl);
|
|
new import_obsidian4.Setting(containerEl).setName("Trigger in code blocks").setDesc("Whether the LaTeX provider should trigger after dollar signs which are enclosed in code blocks (for example ```$\\fr```).").addToggle((toggle) => toggle.setValue(this.plugin.settings.latexTriggerInCodeBlocks).onChange((val) => __async(this, null, function* () {
|
|
this.plugin.settings.latexTriggerInCodeBlocks = val;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new import_obsidian4.Setting(containerEl).setName("Ignore case").setDesc("Whether the LaTeX provider should ignore the casing of the typed text. If so, the input 'MaThbb' could suggest 'mathbb'.").addToggle((toggle) => toggle.setValue(this.plugin.settings.latexIgnoreCase).onChange((val) => __async(this, null, function* () {
|
|
this.plugin.settings.latexIgnoreCase = val;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new import_obsidian4.Setting(containerEl).setName("Minimum word trigger length").setDesc("The minimum length a query has to be, to trigger suggestions.").addText((text) => {
|
|
text.inputEl.type = "number";
|
|
text.setValue(this.plugin.settings.latexMinWordTriggerLength + "").onChange((val) => __async(this, null, function* () {
|
|
if (!val || val.length < 1)
|
|
return;
|
|
this.plugin.settings.latexMinWordTriggerLength = parseInt(val);
|
|
yield this.plugin.saveSettings();
|
|
}));
|
|
});
|
|
new import_obsidian4.Setting(containerEl).setName("Front matter provider").addExtraButton((button) => button.setIcon("link").setTooltip("Obsidian Front-Matter wiki").onClick(() => window.open("https://help.obsidian.md/Advanced+topics/YAML+front+matter"))).setHeading();
|
|
this.createEnabledSetting("frontMatterProviderEnabled", "Whether the front matter provider is enabled", containerEl);
|
|
new import_obsidian4.Setting(containerEl).setName("Ignore case").setDesc("Whether the Front matter provider should ignore the casing of the typed text. If so, the input 'MaThbb' could suggest 'mathbb'.").addToggle((toggle) => toggle.setValue(this.plugin.settings.frontMatterIgnoreCase).onChange((val) => __async(this, null, function* () {
|
|
this.plugin.settings.frontMatterIgnoreCase = val;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new import_obsidian4.Setting(containerEl).setName("Add suffix to tag completion").setDesc("Whether each completed tag should be suffixed with a comma or a newline (when typing in a multi-line list). Allows faster insertion of multiple tags.").addToggle((toggle) => toggle.setValue(this.plugin.settings.frontMatterTagAppendSuffix).onChange((val) => __async(this, null, function* () {
|
|
this.plugin.settings.frontMatterTagAppendSuffix = val;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new import_obsidian4.Setting(containerEl).setName("File scanner provider").setHeading().addExtraButton((button) => button.setIcon("search").setTooltip("Immediately scan all .md files currently in your vault.").onClick(() => {
|
|
new ConfirmationModal(
|
|
this.plugin.app,
|
|
"Start scanning?",
|
|
"Depending on the size of your vault and computer, this may take a while.",
|
|
(button2) => button2.setButtonText("Scan").setCta(),
|
|
() => __async(this, null, function* () {
|
|
yield FileScanner.scanFiles(this.plugin.settings, this.plugin.app.vault.getMarkdownFiles());
|
|
})
|
|
).open();
|
|
})).addExtraButton((button) => button.setIcon("trash").setTooltip("Delete all known words.").onClick(() => __async(this, null, function* () {
|
|
new ConfirmationModal(
|
|
this.plugin.app,
|
|
"Delete all known words?",
|
|
"This will delete all words that have been scanned. No suggestions from this provider will show up anymore until new files are scanned.",
|
|
(button2) => button2.setButtonText("Delete").setWarning(),
|
|
() => __async(this, null, function* () {
|
|
yield FileScanner.deleteAllWords(this.plugin.app.vault);
|
|
})
|
|
).open();
|
|
})));
|
|
this.createEnabledSetting("fileScannerProviderEnabled", "Whether or not the file scanner provider is enabled.", containerEl);
|
|
new import_obsidian4.Setting(containerEl).setName("Scan active file").setDesc("If this setting is enabled, the currently opened file will be scanned to find new words.").addToggle((toggle) => toggle.setValue(this.plugin.settings.fileScannerScanCurrent).onChange((val) => __async(this, null, function* () {
|
|
this.plugin.settings.fileScannerScanCurrent = val;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
new import_obsidian4.Setting(containerEl).setName("Word list provider").setHeading();
|
|
this.createEnabledSetting("wordListProviderEnabled", "Whether or not the word list provider is enabled", containerEl);
|
|
const fileInput = createEl("input", {
|
|
attr: {
|
|
type: "file"
|
|
}
|
|
});
|
|
fileInput.onchange = () => __async(this, null, function* () {
|
|
const files = fileInput.files;
|
|
if (files.length < 1)
|
|
return;
|
|
let changed = false;
|
|
for (let i = 0; i < files.length; i++) {
|
|
const file = files[i];
|
|
const text = yield file.text();
|
|
const success = yield WordList.importWordList(this.app.vault, file.name, text);
|
|
changed || (changed = success);
|
|
if (!success)
|
|
new import_obsidian4.Notice("Unable to import " + file.name + " because it already exists!");
|
|
}
|
|
if (!changed)
|
|
return;
|
|
yield this.reloadWords();
|
|
this.display();
|
|
});
|
|
new import_obsidian4.Setting(containerEl).setName("Word list files").setDesc("A list of files which contain words to be used as suggestions. Each word should be on its own line.").addExtraButton((button) => button.setIcon("switch").setTooltip("Reload").onClick(() => __async(this, null, function* () {
|
|
yield this.reloadWords();
|
|
this.display();
|
|
}))).addButton((button) => {
|
|
button.buttonEl.appendChild(fileInput);
|
|
button.setButtonText("+").setCta().onClick(() => fileInput.click());
|
|
});
|
|
const wordListDiv = containerEl.createDiv();
|
|
WordList.getRelativeFilePaths(this.app.vault).then((names) => {
|
|
for (const name of names) {
|
|
new import_obsidian4.Setting(wordListDiv).setName(name).addExtraButton(
|
|
(button) => button.setIcon("trash").setTooltip("Remove").onClick(() => __async(this, null, function* () {
|
|
new ConfirmationModal(
|
|
this.app,
|
|
"Delete " + name + "?",
|
|
"The file will be removed and the words inside of it won't show up as suggestions anymore.",
|
|
(button2) => button2.setButtonText("Delete").setWarning(),
|
|
() => __async(this, null, function* () {
|
|
yield WordList.deleteWordList(this.app.vault, name);
|
|
yield this.reloadWords();
|
|
this.display();
|
|
})
|
|
).open();
|
|
}))
|
|
).settingEl.addClass("completr-settings-list-item");
|
|
}
|
|
});
|
|
}
|
|
reloadWords() {
|
|
return __async(this, null, function* () {
|
|
if (this.isReloadingWords)
|
|
return;
|
|
this.isReloadingWords = true;
|
|
const count = yield WordList.loadFromFiles(this.app.vault, this.plugin.settings);
|
|
this.isReloadingWords = false;
|
|
new import_obsidian4.Notice(`Loaded ${count} words`);
|
|
});
|
|
}
|
|
createEnabledSetting(propertyName, desc, container) {
|
|
new import_obsidian4.Setting(container).setName("Enabled").setDesc(desc).addToggle((toggle) => toggle.setValue(this.plugin.settings[propertyName]).onChange((val) => __async(this, null, function* () {
|
|
this.plugin.settings[propertyName] = val;
|
|
yield this.plugin.saveSettings();
|
|
})));
|
|
}
|
|
};
|
|
var ConfirmationModal = class extends import_obsidian4.Modal {
|
|
constructor(app, title, body, buttonCallback, clickCallback) {
|
|
super(app);
|
|
this.titleEl.setText(title);
|
|
this.contentEl.setText(body);
|
|
new import_obsidian4.Setting(this.modalEl).addButton((button) => {
|
|
buttonCallback(button);
|
|
button.onClick(() => __async(this, null, function* () {
|
|
yield clickCallback();
|
|
this.close();
|
|
}));
|
|
}).addButton((button) => button.setButtonText("Cancel").onClick(() => this.close())).settingEl.addClass("completr-settings-no-border");
|
|
}
|
|
};
|
|
|
|
// src/main.ts
|
|
var import_view3 = require("@codemirror/view");
|
|
var CompletrPlugin = class extends import_obsidian5.Plugin {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.onFileOpened = (file) => {
|
|
if (!this.settings.fileScannerProviderEnabled || !this.settings.fileScannerScanCurrent || !file)
|
|
return;
|
|
FileScanner.scanFile(this.settings, file, true);
|
|
};
|
|
}
|
|
onload() {
|
|
return __async(this, null, function* () {
|
|
var _a;
|
|
yield this.loadSettings();
|
|
this.snippetManager = new SnippetManager();
|
|
this._suggestionPopup = new SuggestionPopup(this.app, this.settings, this.snippetManager);
|
|
this.registerEditorSuggest(this._suggestionPopup);
|
|
this.registerEvent(this.app.workspace.on("file-open", this.onFileOpened, this));
|
|
this.registerEvent(this.app.metadataCache.on("changed", FrontMatter.onCacheChange, FrontMatter));
|
|
this.app.workspace.onLayoutReady(() => FrontMatter.loadYAMLKeyCompletions(this.app.metadataCache, this.app.vault.getMarkdownFiles()));
|
|
this.registerEditorExtension(markerStateField);
|
|
this.registerEditorExtension(import_view3.EditorView.updateListener.of(new CursorActivityListener(this.snippetManager, this._suggestionPopup).listener));
|
|
this.addSettingTab(new CompletrSettingsTab(this.app, this));
|
|
this.setupCommands();
|
|
if ((_a = this.app.vault.config) == null ? void 0 : _a.legacyEditor) {
|
|
console.log("Completr: Without Live Preview enabled, most features of Completr will not work properly!");
|
|
}
|
|
});
|
|
}
|
|
setupCommands() {
|
|
const app = this.app;
|
|
app.scope.keys = [];
|
|
const isHotkeyMatch = (hotkey, context, id) => {
|
|
const modifiers = hotkey.modifiers, key = hotkey.key;
|
|
if (modifiers !== null && (id.contains("completr-bypass") ? !context.modifiers.contains(modifiers) : modifiers !== context.modifiers))
|
|
return false;
|
|
return !key || (key === context.vkey || !(!context.key || key.toLowerCase() !== context.key.toLowerCase()));
|
|
};
|
|
this.app.scope.register(null, null, (e, t) => {
|
|
const hotkeyManager = app.hotkeyManager;
|
|
hotkeyManager.bake();
|
|
for (let bakedHotkeys = hotkeyManager.bakedHotkeys, bakedIds = hotkeyManager.bakedIds, r = 0; r < bakedHotkeys.length; r++) {
|
|
const hotkey = bakedHotkeys[r];
|
|
const id = bakedIds[r];
|
|
if (isHotkeyMatch(hotkey, t, id)) {
|
|
const command = app.commands.findCommand(id);
|
|
if (!command || e.repeat && !command.repeatable) {
|
|
continue;
|
|
} else if (command.isVisible && !command.isVisible()) {
|
|
continue;
|
|
} else if (id.contains("completr-bypass")) {
|
|
this._suggestionPopup.close();
|
|
const validMods = t.modifiers.replace(new RegExp(`${hotkey.modifiers},*`), "").split(",");
|
|
let event = new KeyboardEvent("keydown", {
|
|
key: hotkeyManager.defaultKeys[id][0].key,
|
|
ctrlKey: validMods.contains("Ctrl"),
|
|
shiftKey: validMods.contains("Shift"),
|
|
altKey: validMods.contains("Alt"),
|
|
metaKey: validMods.contains("Meta")
|
|
});
|
|
e.target.dispatchEvent(event);
|
|
return false;
|
|
}
|
|
if (app.commands.executeCommandById(id))
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: "completr-open-suggestion-popup",
|
|
name: "Open suggestion popup",
|
|
hotkeys: [
|
|
{
|
|
key: " ",
|
|
modifiers: ["Mod"]
|
|
}
|
|
],
|
|
editorCallback: (editor) => {
|
|
this._suggestionPopup.trigger(editor, this.app.workspace.getActiveFile(), true);
|
|
},
|
|
isVisible: () => !this._suggestionPopup.isVisible()
|
|
});
|
|
this.addCommand({
|
|
id: "completr-select-next-suggestion",
|
|
name: "Select next suggestion",
|
|
hotkeys: [
|
|
{
|
|
key: "ArrowDown",
|
|
modifiers: []
|
|
}
|
|
],
|
|
repeatable: true,
|
|
editorCallback: (editor) => {
|
|
this.suggestionPopup.selectNextItem(1 /* NEXT */);
|
|
},
|
|
isVisible: () => this._suggestionPopup.isVisible()
|
|
});
|
|
this.addCommand({
|
|
id: "completr-select-previous-suggestion",
|
|
name: "Select previous suggestion",
|
|
hotkeys: [
|
|
{
|
|
key: "ArrowUp",
|
|
modifiers: []
|
|
}
|
|
],
|
|
repeatable: true,
|
|
editorCallback: (editor) => {
|
|
this.suggestionPopup.selectNextItem(-1 /* PREVIOUS */);
|
|
},
|
|
isVisible: () => this._suggestionPopup.isVisible()
|
|
});
|
|
this.addCommand({
|
|
id: "completr-insert-selected-suggestion",
|
|
name: "Insert selected suggestion",
|
|
hotkeys: [
|
|
{
|
|
key: "Enter",
|
|
modifiers: []
|
|
}
|
|
],
|
|
editorCallback: (editor) => {
|
|
this.suggestionPopup.applySelectedItem();
|
|
},
|
|
isVisible: () => this._suggestionPopup.isVisible()
|
|
});
|
|
this.addCommand({
|
|
id: "completr-bypass-enter-key",
|
|
name: "Bypass the popup and press Enter",
|
|
hotkeys: [
|
|
{
|
|
key: "Enter",
|
|
modifiers: ["Ctrl"]
|
|
}
|
|
],
|
|
editorCallback: (editor) => {
|
|
},
|
|
isVisible: () => this._suggestionPopup.isVisible()
|
|
});
|
|
this.addCommand({
|
|
id: "completr-bypass-tab-key",
|
|
name: "Bypass the popup and press Tab",
|
|
hotkeys: [
|
|
{
|
|
key: "Tab",
|
|
modifiers: ["Ctrl"]
|
|
}
|
|
],
|
|
editorCallback: (editor) => {
|
|
},
|
|
isVisible: () => this._suggestionPopup.isVisible()
|
|
});
|
|
this.addCommand({
|
|
id: "completr-blacklist-current-word",
|
|
name: "Add the currently selected word to the blacklist",
|
|
hotkeys: [
|
|
{
|
|
key: "D",
|
|
modifiers: ["Shift"]
|
|
}
|
|
],
|
|
editorCallback: (editor) => {
|
|
SuggestionBlacklist.add(this._suggestionPopup.getSelectedItem());
|
|
SuggestionBlacklist.saveData(this.app.vault);
|
|
this._suggestionPopup.trigger(editor, this.app.workspace.getActiveFile(), true);
|
|
},
|
|
isVisible: () => this._suggestionPopup.isVisible()
|
|
});
|
|
this.addCommand({
|
|
id: "completr-close-suggestion-popup",
|
|
name: "Close suggestion popup",
|
|
hotkeys: [
|
|
{
|
|
key: "Escape",
|
|
modifiers: []
|
|
}
|
|
],
|
|
editorCallback: (editor) => {
|
|
this.suggestionPopup.close();
|
|
},
|
|
isVisible: () => this._suggestionPopup.isVisible()
|
|
});
|
|
this.addCommand({
|
|
id: "completr-jump-to-next-snippet-placeholder",
|
|
name: "Jump to next snippet placeholder",
|
|
hotkeys: [
|
|
{
|
|
key: "Enter",
|
|
modifiers: []
|
|
}
|
|
],
|
|
editorCallback: (editor, view) => {
|
|
const placeholder = this.snippetManager.placeholderAtPos(editor.getCursor());
|
|
if (!placeholder)
|
|
return;
|
|
const placeholderEnd = posFromIndex(editorToCodeMirrorState(placeholder.editor).doc, placeholder.marker.to);
|
|
if (!this.snippetManager.consumeAndGotoNextMarker(editor)) {
|
|
editor.setSelections([{
|
|
anchor: __spreadProps(__spreadValues({}, placeholderEnd), {
|
|
ch: Math.min(editor.getLine(placeholderEnd.line).length, placeholderEnd.ch + 1)
|
|
})
|
|
}]);
|
|
}
|
|
},
|
|
isVisible: () => {
|
|
const view = this.app.workspace.getActiveViewOfType(import_obsidian5.MarkdownView);
|
|
if (!view)
|
|
return false;
|
|
const placeholder = this.snippetManager.placeholderAtPos(view.editor.getCursor());
|
|
return placeholder != null;
|
|
}
|
|
});
|
|
}
|
|
onunload() {
|
|
return __async(this, null, function* () {
|
|
this.snippetManager.onunload();
|
|
yield FileScanner.saveData(this.app.vault);
|
|
});
|
|
}
|
|
loadSettings() {
|
|
return __async(this, null, function* () {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
|
|
SuggestionBlacklist.loadData(this.app.vault).then(() => {
|
|
WordList.loadFromFiles(this.app.vault, this.settings);
|
|
FileScanner.loadData(this.app.vault);
|
|
Latex.loadCommands(this.app.vault);
|
|
});
|
|
});
|
|
}
|
|
get suggestionPopup() {
|
|
return this._suggestionPopup;
|
|
}
|
|
saveSettings() {
|
|
return __async(this, null, function* () {
|
|
yield this.saveData(this.settings);
|
|
});
|
|
}
|
|
};
|
|
var CursorActivityListener = class {
|
|
constructor(snippetManager, suggestionPopup) {
|
|
this.cursorTriggeredByChange = false;
|
|
this.lastCursorLine = -1;
|
|
this.listener = (update) => {
|
|
if (update.docChanged) {
|
|
this.handleDocChange();
|
|
}
|
|
if (update.selectionSet) {
|
|
this.handleCursorActivity(posFromIndex(update.state.doc, update.state.selection.main.head));
|
|
}
|
|
};
|
|
this.handleDocChange = () => {
|
|
this.cursorTriggeredByChange = true;
|
|
};
|
|
this.handleCursorActivity = (cursor) => {
|
|
if (this.lastCursorLine == cursor.line + 1)
|
|
this.suggestionPopup.preventNextTrigger();
|
|
this.lastCursorLine = cursor.line;
|
|
if (!this.snippetManager.placeholderAtPos(cursor)) {
|
|
this.snippetManager.clearAllPlaceholders();
|
|
}
|
|
if (this.cursorTriggeredByChange) {
|
|
this.cursorTriggeredByChange = false;
|
|
return;
|
|
}
|
|
this.suggestionPopup.close();
|
|
};
|
|
this.snippetManager = snippetManager;
|
|
this.suggestionPopup = suggestionPopup;
|
|
}
|
|
};
|