185 lines
6.1 KiB
TypeScript
185 lines
6.1 KiB
TypeScript
import { PublishResponse } from "@base/networking/PublishResponse";
|
|
import { Editor, EditorPosition, parseYaml, stringifyYaml } from "obsidian";
|
|
|
|
export const YAML_REGEX = /^---\n(?:((?:.|\n)*?)\n)?---(?=\n|$)/;
|
|
|
|
type CommonUpdateParam = {
|
|
editor: Editor;
|
|
key: string;
|
|
value: string;
|
|
action: 'replace' | 'insert' | 'remove';
|
|
}
|
|
|
|
type BulkUpdateParam = {
|
|
editor: Editor;
|
|
updateDatas: Record<string, unknown>;
|
|
removeDatas: string[];
|
|
action: 'bulk'
|
|
}
|
|
|
|
export interface FrontmatterServiceInterface {
|
|
updateFrontmatter(response: PublishResponse, editor: Editor): void
|
|
}
|
|
|
|
export interface FrontmatterServiceDelegate {
|
|
frontmatterUpdateDidSucceed(): void
|
|
|
|
frontmatterUpdateDidFail(error: Error): void
|
|
}
|
|
|
|
export class FrontmatterService implements FrontmatterServiceInterface {
|
|
|
|
// Properties
|
|
|
|
private delegate?: FrontmatterServiceDelegate
|
|
|
|
// Life cycle
|
|
|
|
constructor(delegate?: FrontmatterServiceDelegate) {
|
|
this.delegate = delegate
|
|
}
|
|
|
|
// Public
|
|
|
|
public async updateFrontmatter(response: PublishResponse, editor: Editor) {
|
|
const yaml = this.getObjectYaml(editor)
|
|
|
|
this.upsert("url", response.url, false, editor)
|
|
this.delegate?.frontmatterUpdateDidSucceed()
|
|
}
|
|
|
|
|
|
// Get objectify yaml of current file
|
|
getObjectYaml(editor: Editor) {
|
|
const stringYaml = this.getYaml(editor);
|
|
|
|
return stringYaml? parseYaml(stringYaml.slice(4, -4)): {}
|
|
}
|
|
|
|
// Exchange item position in array
|
|
itemMove<T>(arr: T[], itemIdx1: number, itemIdx2: number): void {
|
|
[arr[itemIdx1], arr[itemIdx2]] = [arr[itemIdx2], arr[itemIdx1]];
|
|
}
|
|
|
|
// Add item in specific position
|
|
itemAdd<T>(arr: T[], itemIdx: number, item: T): void {
|
|
arr.splice(itemIdx, 0, item)
|
|
}
|
|
|
|
// Delete specific item in array
|
|
itemDelete<T>(arr: T[], itemIndex: number): void {
|
|
arr.splice(itemIndex, 1);
|
|
}
|
|
|
|
// Get yaml section
|
|
getYaml(editor: Editor): string {
|
|
const matchResult = editor.getValue().match(YAML_REGEX);
|
|
|
|
return matchResult?.[0] ?? '';
|
|
}
|
|
|
|
generateActionKeyword(data: CommonUpdateParam | BulkUpdateParam) {
|
|
const {editor, action} = data;
|
|
const yamlSection = this.getYaml(editor);
|
|
const yaml = yamlSection.slice(4, -3);
|
|
const objectYaml = this.getObjectYaml(editor);
|
|
const objectSnippet: Record<string, unknown> = {};
|
|
|
|
if (action === 'replace') objectSnippet[data.key] = data.value;
|
|
|
|
if (action === 'insert') {
|
|
if (objectYaml[data.key] instanceof Array) {
|
|
console.log('inserting into array')
|
|
objectSnippet[data.key] = [...objectYaml[data.key], data.value];
|
|
} else {
|
|
console.log('inserting')
|
|
//objectSnippet[data.key] = [data.value];
|
|
objectSnippet[data.key] = data.value;
|
|
}
|
|
}
|
|
if (action === 'remove') {
|
|
if (objectYaml[data.key] instanceof Array) {
|
|
const newValue = objectYaml[data.key].filter((val: string) => val !== data.value);
|
|
objectSnippet[data.key] = newValue.length? newValue: null;
|
|
} else {
|
|
objectSnippet[data.key] = null;
|
|
}
|
|
}
|
|
|
|
if (action === 'bulk') {
|
|
Object.entries(data.updateDatas).forEach(([key, value]) => objectSnippet[key] = value );
|
|
data.removeDatas.forEach((key) => objectSnippet[key] = null)
|
|
}
|
|
|
|
let replacement = `---\n${this.generateReplacement(yaml, objectSnippet)}---`;
|
|
const startPosition: EditorPosition = {line: 0, ch: 0};
|
|
const endPosition: EditorPosition = editor.offsetToPos(yamlSection.length);
|
|
|
|
// Make sure there is at least one newline character
|
|
// after the end of the frontmatter
|
|
const charAfterYaml = editor.getRange(endPosition, {ch: endPosition.ch + 1, line: endPosition.line})
|
|
replacement = charAfterYaml == "\n" ? replacement : replacement + "\n"
|
|
|
|
return {replacement, startPosition, endPosition}
|
|
}
|
|
|
|
generateReplacement(yaml: string, snippet: Record<string, unknown>) {
|
|
return Object.entries(snippet).reduce((temp, [key, value]) => {
|
|
const YAML_FIELD_REGEX = new RegExp(`(${key} *:).+?\\n(?=\\S|$)`, 'gs');
|
|
|
|
const replacement = (value === null)? '': stringifyYaml({[key]: value});
|
|
|
|
return temp.match(YAML_FIELD_REGEX)? temp.replace(YAML_FIELD_REGEX, replacement): `${temp}${replacement}`;
|
|
}, yaml)
|
|
}
|
|
|
|
flatYamlFields(yaml: string, flatFields: string[]): string {
|
|
const objectYaml = parseYaml(yaml.slice(4, -4));
|
|
|
|
return flatFields.reduce((temp, key) => {
|
|
const YAML_FIELD_REGEX = new RegExp(`(${key}:).+?(?=\\n\\S|$)`, 'gs');
|
|
|
|
return temp.match(YAML_FIELD_REGEX)? temp.replace(YAML_FIELD_REGEX, `$1 [${objectYaml[key].join(', ')}]`): temp;
|
|
}, yaml)
|
|
}
|
|
|
|
replace(key: string, value: string, editor: Editor): void {
|
|
const {replacement, startPosition, endPosition} = this.generateActionKeyword({key, value, editor, action: 'replace'});
|
|
|
|
editor.replaceRange(replacement, startPosition, endPosition)
|
|
}
|
|
|
|
insert(key: string, value: string, flat: boolean, editor: Editor): void {
|
|
const {replacement, startPosition, endPosition} = this.generateActionKeyword({key, value, editor, action: 'insert'});
|
|
|
|
const postProcessedReplacement = flat? this.flatYamlFields(replacement, [key]): replacement;
|
|
|
|
editor.replaceRange(postProcessedReplacement, startPosition, endPosition)
|
|
}
|
|
|
|
upsert(key: string, value: string, flat: boolean, editor: Editor): void {
|
|
const yaml = this.getObjectYaml(editor)
|
|
|
|
if (yaml[key]) {
|
|
this.replace(key, value, editor)
|
|
} else {
|
|
this.insert(key, value, flat, editor)
|
|
}
|
|
}
|
|
|
|
remove(key: string, value: string, flat: boolean, editor: Editor): void {
|
|
const {replacement, startPosition, endPosition} = this.generateActionKeyword({key, value, editor, action: 'remove'});
|
|
|
|
const postProcessedReplacement = flat? this.flatYamlFields(replacement, [key]): replacement;
|
|
|
|
editor.replaceRange(postProcessedReplacement, startPosition, endPosition)
|
|
}
|
|
|
|
bulkUpdate(updateDatas: Record<string, unknown>, removeDatas: string[], flatFields: string[], editor: Editor): void {
|
|
const {replacement, startPosition, endPosition} = this.generateActionKeyword({updateDatas, removeDatas, editor, action: 'bulk'});
|
|
|
|
const flattedReplacement = this.flatYamlFields(replacement, flatFields);
|
|
|
|
editor.replaceRange(flattedReplacement, startPosition, endPosition);
|
|
}
|
|
} |