insta-toc
Version:
Simultaneously generate, update, and maintain a table of contents for your notes in real time.
104 lines (76 loc) • 2.88 kB
text/typescript
export type CompletionMatchMode = "startsWith" | "includes";
export function stripYamlInlineComment(value: string): string {
let inSingle = false;
let inDouble = false;
for (let i = 0; i < value.length; i += 1) {
const char = value[i];
if (char === "'" && !inDouble) {
if (inSingle && value[i + 1] === "'") {
i += 1;
continue;
}
inSingle = !inSingle;
continue;
}
if (char === "\"" && !inSingle) {
let backslashCount = 0;
for (let j = i - 1; j >= 0 && value[j] === "\\"; j -= 1) {
backslashCount += 1;
}
if (backslashCount % 2 === 0) {
inDouble = !inDouble;
}
continue;
}
if (char === "#" && !inSingle && !inDouble) {
const previousChar = i === 0 ? " " : value[i - 1];
if (/\s/.test(previousChar)) {
return value.slice(0, i).trimEnd();
}
}
}
return value.trimEnd();
}
export function quoteYamlSingleQuoted(value: string): string {
return `'${value.replace(/'/g, "''")}'`;
}
function unquoteYamlSingleQuoted(value: string): string {
return value.slice(1, -1).replace(/''/g, "'");
}
function unquoteYamlDoubleQuoted(value: string): string {
return value.slice(1, -1).replace(/\\(["\\])/g, "$1");
}
export function parseYamlArrayItemValue(rawValue: string): string {
const trimmedValue = stripYamlInlineComment(rawValue).trim().replace(/''/g, "'").replace(/""/g, "\"");
if (trimmedValue.length === 0) return "";
if (trimmedValue.startsWith("'") && trimmedValue.endsWith("'") && trimmedValue.length > 1) {
return unquoteYamlSingleQuoted(trimmedValue);
}
if (trimmedValue.startsWith("\"") && trimmedValue.endsWith("\"") && trimmedValue.length > 1) {
return unquoteYamlDoubleQuoted(trimmedValue);
}
return trimmedValue;
}
export function normalizeYamlArrayItemQuery(rawValue: string): string {
const trimmedValue = stripYamlInlineComment(rawValue).trimStart();
if (trimmedValue.length === 0) return "";
if (trimmedValue.startsWith("'")) {
return trimmedValue.slice(1).replace(/''/g, "'");
}
if (trimmedValue.startsWith("\"")) {
return trimmedValue.slice(1).replace(/\\(["\\])/g, "$1");
}
return trimmedValue;
}
export function matchesCompletionQuery(
value: string,
query: string,
mode: CompletionMatchMode = "startsWith"
): boolean {
const normalizedQuery = query.trim().toLowerCase();
if (normalizedQuery.length === 0) return true;
const normalizedValue = value.toLowerCase();
return mode === "includes"
? normalizedValue.includes(normalizedQuery)
: normalizedValue.startsWith(normalizedQuery);
}