Skip to content

Commit

Permalink
feat(Threading): ✨ Option to threadUnderCursor (Fix #276)
Browse files Browse the repository at this point in the history
  • Loading branch information
SkepticMystic committed Feb 5, 2022
1 parent f8dbdff commit 5b0ddde
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 85 deletions.
110 changes: 70 additions & 40 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3466,6 +3466,7 @@ const DEFAULT_SETTINGS = {
threadIntoNewPane: false,
threadingTemplate: "{{field}} of {{current}}",
threadingDirTemplates: { up: "", same: "", down: "", next: "", prev: "" },
threadUnderCursor: false,
trailSeperator: "→",
treatCurrNodeAsImpliedSibling: false,
trimDendronNotes: false,
Expand Down Expand Up @@ -24889,36 +24890,26 @@ async function jumpToFirstDir(plugin, dir) {
await plugin.app.workspace.activeLeaf.openFile(toFile);
}

async function thread(plugin, field) {
var _a;
const { app, settings } = plugin;
const { userHiers, writeBCsInline, threadingTemplate, dateFormat, threadingDirTemplates, threadIntoNewPane, } = settings;
const currFile = app.workspace.getActiveFile();
if (!currFile)
return;
const newFileParent = app.fileManager.getNewFileParent(currFile.path);
const dir = getFieldInfo(userHiers, field).fieldDir;
const oppField = getOppFields(userHiers, field, dir)[0];
let newBasename = threadingTemplate
? threadingTemplate
.replace("{{current}}", currFile.basename)
.replace("{{field}}", field)
.replace("{{dir}}", dir)
//@ts-ignore
.replace("{{date}}", moment().format(dateFormat))
: "Untitled";
let i = 1;
while (app.metadataCache.getFirstLinkpathDest(newBasename, "")) {
const resolveThreadingNameTemplate = (template, currFile, field, dir, dateFormat) => template
? template
.replace("{{current}}", currFile.basename)
.replace("{{field}}", field)
.replace("{{dir}}", dir)
//@ts-ignore
.replace("{{date}}", moment().format(dateFormat))
: "Untitled";
function makeFilenameUnique(app, filename) {
let i = 1, newName = filename;
while (app.metadataCache.getFirstLinkpathDest(newName, "")) {
if (i === 1)
newBasename += ` ${i}`;
newName += ` ${i}`;
else
newBasename = newBasename.slice(0, -2) + ` ${i}`;
newName = newName.slice(0, -2) + ` ${i}`;
i++;
}
const crumb = writeBCsInline
? `${oppField}:: [[${currFile.basename}]]`
: `---\n${oppField}: ['${currFile.basename}']\n---`;
const templatePath = threadingDirTemplates[dir];
return newName;
}
async function resolveThreadingContentTemplate(app, writeBCsInline, templatePath, oppField, currFile, crumb) {
let newContent = crumb;
if (templatePath) {
const templateFile = app.metadataCache.getFirstLinkpathDest(templatePath, "");
Expand All @@ -24927,6 +24918,25 @@ async function thread(plugin, field) {
? `${oppField}:: [[${currFile.basename}]]`
: `${oppField}: ['${currFile.basename}']`);
}
return newContent;
}
async function thread(plugin, field) {
var _a;
const { app, settings } = plugin;
const { userHiers, threadingTemplate, dateFormat, threadIntoNewPane, threadingDirTemplates, threadUnderCursor, writeBCsInline, } = settings;
const currFile = app.workspace.getActiveFile();
if (!currFile)
return;
const newFileParent = app.fileManager.getNewFileParent(currFile.path);
const dir = getFieldInfo(userHiers, field).fieldDir;
const oppField = getOppFields(userHiers, field, dir)[0];
let newBasename = resolveThreadingNameTemplate(threadingTemplate, currFile, field, dir, dateFormat);
newBasename = makeFilenameUnique(app, newBasename);
const oppCrumb = writeBCsInline
? `${oppField}:: [[${currFile.basename}]]`
: `---\n${oppField}: ['${currFile.basename}']\n---`;
const templatePath = threadingDirTemplates[dir];
const newContent = await resolveThreadingContentTemplate(app, writeBCsInline, templatePath, oppField, currFile, oppCrumb);
const newFile = await app.vault.create(obsidian.normalizePath(`${newFileParent.path}/${newBasename}.md`), newContent);
if (!writeBCsInline) {
const { api } = (_a = app.plugins.plugins.metaedit) !== null && _a !== void 0 ? _a : {};
Expand All @@ -24937,16 +24947,23 @@ async function thread(plugin, field) {
await createOrUpdateYaml(field, newFile.basename, currFile, app.metadataCache.getFileCache(currFile).frontmatter, api);
}
else {
// TODO Check if this note already has this field
let content = await app.vault.read(currFile);
const splits = splitAtYaml(content);
content =
splits[0] +
(splits[0].length ? "\n" : "") +
`${field}:: [[${newFile.basename}]]` +
(splits[1].length ? "\n" : "") +
splits[1];
await app.vault.modify(currFile, content);
const crumb = `${field}:: [[${newFile.basename}]]`;
const { editor } = app.workspace.activeLeaf.view;
if (threadUnderCursor || !editor) {
editor.replaceRange(crumb, editor.getCursor());
}
else {
// TODO Check if this note already has this field
let content = await app.vault.read(currFile);
const splits = splitAtYaml(content);
content =
splits[0] +
(splits[0].length ? "\n" : "") +
crumb +
(splits[1].length ? "\n" : "") +
splits[1];
await app.vault.modify(currFile, content);
}
}
const leaf = threadIntoNewPane
? app.workspace.splitActiveLeaf()
Expand Down Expand Up @@ -39452,10 +39469,23 @@ function addThreadingSettings(plugin, cmdsDetails) {
});
new obsidian.Setting(threadingDetails)
.setName("Open new threads in new pane or current pane")
.addToggle((tog) => tog.onChange(async (value) => {
settings.threadIntoNewPane = value;
await plugin.saveSettings();
}));
.addToggle((tog) => {
tog.setValue(settings.threadIntoNewPane);
tog.onChange(async (value) => {
settings.threadIntoNewPane = value;
await plugin.saveSettings();
});
});
new obsidian.Setting(threadingDetails)
.setName("Thread under Cursor")
.setDesc(fragWithHTML("If the setting <code>Write Breadcrumbs Inline</code> is enabled, where should the new Breadcrumb be added to the current note? ✅ = Under the cursor, ❌ = At the top of the note (under the yaml, if applicable)"))
.addToggle((tog) => {
tog.setValue(settings.threadUnderCursor);
tog.onChange(async (value) => {
settings.threadUnderCursor = value;
await plugin.saveSettings();
});
});
new obsidian.Setting(threadingDetails)
.setName("New Note Name Template")
.setDesc(fragWithHTML(`When threading into a new note, choose the template for the new note name.</br>
Expand Down
132 changes: 90 additions & 42 deletions src/Commands/threading.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,46 @@
import { normalizePath, Notice } from "obsidian";
import { App, normalizePath, Notice, TFile } from "obsidian";
import type { Directions } from "../interfaces";
import type BCPlugin from "../main";
import { getFieldInfo, getOppFields } from "../Utils/HierUtils";
import { createOrUpdateYaml, splitAtYaml } from "../Utils/ObsidianUtils";

export async function thread(plugin: BCPlugin, field: string) {
const { app, settings } = plugin;
const {
userHiers,
writeBCsInline,
threadingTemplate,
dateFormat,
threadingDirTemplates,
threadIntoNewPane,
} = settings;

const currFile = app.workspace.getActiveFile();
if (!currFile) return;

const newFileParent = app.fileManager.getNewFileParent(currFile.path);

const dir = getFieldInfo(userHiers, field).fieldDir;
const oppField = getOppFields(userHiers, field, dir)[0];

let newBasename = threadingTemplate
? threadingTemplate
const resolveThreadingNameTemplate = (
template: string,
currFile: TFile,
field: string,
dir: Directions,
dateFormat: string
) =>
template
? template
.replace("{{current}}", currFile.basename)
.replace("{{field}}", field)
.replace("{{dir}}", dir)
//@ts-ignore
.replace("{{date}}", moment().format(dateFormat))
: "Untitled";

let i = 1;
while (app.metadataCache.getFirstLinkpathDest(newBasename, "")) {
if (i === 1) newBasename += ` ${i}`;
else newBasename = newBasename.slice(0, -2) + ` ${i}`;
function makeFilenameUnique(app: App, filename: string) {
let i = 1,
newName = filename;
while (app.metadataCache.getFirstLinkpathDest(newName, "")) {
if (i === 1) newName += ` ${i}`;
else newName = newName.slice(0, -2) + ` ${i}`;
i++;
}
return newName;
}

const crumb = writeBCsInline
? `${oppField}:: [[${currFile.basename}]]`
: `---\n${oppField}: ['${currFile.basename}']\n---`;

const templatePath = threadingDirTemplates[dir];
async function resolveThreadingContentTemplate(
app: App,
writeBCsInline: boolean,
templatePath: string,
oppField: string,
currFile: TFile,
crumb: string
) {
let newContent = crumb;

if (templatePath) {
const templateFile = app.metadataCache.getFirstLinkpathDest(
templatePath,
Expand All @@ -58,6 +55,51 @@ export async function thread(plugin: BCPlugin, field: string) {
: `${oppField}: ['${currFile.basename}']`
);
}
return newContent;
}

export async function thread(plugin: BCPlugin, field: string) {
const { app, settings } = plugin;
const {
userHiers,
threadingTemplate,
dateFormat,
threadIntoNewPane,
threadingDirTemplates,
threadUnderCursor,
writeBCsInline,
} = settings;

const currFile = app.workspace.getActiveFile();
if (!currFile) return;

const newFileParent = app.fileManager.getNewFileParent(currFile.path);

const dir = getFieldInfo(userHiers, field).fieldDir;
const oppField = getOppFields(userHiers, field, dir)[0];

let newBasename = resolveThreadingNameTemplate(
threadingTemplate,
currFile,
field,
dir,
dateFormat
);
newBasename = makeFilenameUnique(app, newBasename);

const oppCrumb = writeBCsInline
? `${oppField}:: [[${currFile.basename}]]`
: `---\n${oppField}: ['${currFile.basename}']\n---`;

const templatePath = threadingDirTemplates[dir];
const newContent = await resolveThreadingContentTemplate(
app,
writeBCsInline,
templatePath,
oppField,
currFile,
oppCrumb
);

const newFile = await app.vault.create(
normalizePath(`${newFileParent.path}/${newBasename}.md`),
Expand All @@ -80,17 +122,23 @@ export async function thread(plugin: BCPlugin, field: string) {
api
);
} else {
// TODO Check if this note already has this field
let content = await app.vault.read(currFile);
const splits = splitAtYaml(content);
content =
splits[0] +
(splits[0].length ? "\n" : "") +
`${field}:: [[${newFile.basename}]]` +
(splits[1].length ? "\n" : "") +
splits[1];

await app.vault.modify(currFile, content);
const crumb = `${field}:: [[${newFile.basename}]]`;
const { editor } = app.workspace.activeLeaf.view;
if (threadUnderCursor || !editor) {
editor.replaceRange(crumb, editor.getCursor());
} else {
// TODO Check if this note already has this field
let content = await app.vault.read(currFile);
const splits = splitAtYaml(content);
content =
splits[0] +
(splits[0].length ? "\n" : "") +
crumb +
(splits[1].length ? "\n" : "") +
splits[1];

await app.vault.modify(currFile, content);
}
}

const leaf = threadIntoNewPane
Expand Down
21 changes: 18 additions & 3 deletions src/Settings/ThreadingSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,27 @@ export function addThreadingSettings(
});
new Setting(threadingDetails)
.setName("Open new threads in new pane or current pane")
.addToggle((tog) =>
.addToggle((tog) => {
tog.setValue(settings.threadIntoNewPane);
tog.onChange(async (value) => {
settings.threadIntoNewPane = value;
await plugin.saveSettings();
})
);
});
});
new Setting(threadingDetails)
.setName("Thread under Cursor")
.setDesc(
fragWithHTML(
"If the setting <code>Write Breadcrumbs Inline</code> is enabled, where should the new Breadcrumb be added to the current note? ✅ = Under the cursor, ❌ = At the top of the note (under the yaml, if applicable)"
)
)
.addToggle((tog) => {
tog.setValue(settings.threadUnderCursor);
tog.onChange(async (value) => {
settings.threadUnderCursor = value;
await plugin.saveSettings();
});
});

new Setting(threadingDetails)
.setName("New Note Name Template")
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ export const DEFAULT_SETTINGS: BCSettings = {
threadIntoNewPane: false,
threadingTemplate: "{{field}} of {{current}}",
threadingDirTemplates: { up: "", same: "", down: "", next: "", prev: "" },
threadUnderCursor: false,
trailSeperator: "→",
treatCurrNodeAsImpliedSibling: false,
trimDendronNotes: false,
Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export interface BCSettings {
threadIntoNewPane: boolean;
threadingTemplate: string;
threadingDirTemplates: { [dir in Directions]: string };
threadUnderCursor: boolean;
trailSeperator: string;
treatCurrNodeAsImpliedSibling: boolean;
trimDendronNotes: boolean;
Expand Down

0 comments on commit 5b0ddde

Please sign in to comment.