Skip to content

Commit

Permalink
Merge pull request #641 from chhoumann/feature/create-line-on-cursor
Browse files Browse the repository at this point in the history
  • Loading branch information
chhoumann authored Feb 14, 2024
2 parents 3abd306 + 419df6b commit f5d6e51
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 94 deletions.
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const NUMBER_REGEX = new RegExp(/^-?[0-9]*$/);

export const CREATE_IF_NOT_FOUND_TOP = "top";
export const CREATE_IF_NOT_FOUND_BOTTOM = "bottom";
export const CREATE_IF_NOT_FOUND_CURSOR = "cursor";

// == Format Syntax == //
export const DATE_REGEX = new RegExp(/{{DATE(\+-?[0-9]+)?}}/i);
Expand Down
93 changes: 36 additions & 57 deletions src/engine/CaptureChoiceEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,53 +30,49 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
app: App,
plugin: QuickAdd,
choice: ICaptureChoice,
private choiceExecutor: IChoiceExecutor
private choiceExecutor: IChoiceExecutor,
) {
super(app);
this.choice = choice;
this.plugin = plugin;
this.formatter = new CaptureChoiceFormatter(
app,
plugin,
choiceExecutor
);
this.formatter = new CaptureChoiceFormatter(app, plugin, choiceExecutor);
}

async run(): Promise<void> {
try {
const filePath = await this.getFormattedPathToCaptureTo(
this.choice.captureToActiveFile
this.choice.captureToActiveFile,
);
const content = this.getCaptureContent();

let getFileAndAddContentFn: typeof this.onFileExists;

if (await this.fileExists(filePath)) {
getFileAndAddContentFn = this.onFileExists.bind(
this
this,
) as typeof this.onFileExists;
} else if (this.choice?.createFileIfItDoesntExist?.enabled) {
getFileAndAddContentFn = this.onCreateFileIfItDoesntExist.bind(
this
this,
) as typeof this.onCreateFileIfItDoesntExist;
} else {
log.logWarning(
`The file ${filePath} does not exist and "Create file if it doesn't exist" is disabled.`
`The file ${filePath} does not exist and "Create file if it doesn't exist" is disabled.`,
);
return;
}

const { file, newFileContent, captureContent } =
await getFileAndAddContentFn(filePath, content);

if (this.choice.captureToActiveFile && !this.choice.prepend && !this.choice.insertAfter.enabled) {
if (
this.choice.captureToActiveFile &&
!this.choice.prepend &&
!this.choice.insertAfter.enabled
) {
// Parse Templater syntax in the capture content.
// If Templater isn't installed, it just returns the capture content.
const content = await templaterParseTemplate(
app,
captureContent,
file
);
const content = await templaterParseTemplate(app, captureContent, file);

appendToCurrentLine(content, this.app);
} else {
Expand All @@ -86,7 +82,7 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
if (this.choice.appendLink) {
const markdownLink = this.app.fileManager.generateMarkdownLink(
file,
""
"",
);

appendToCurrentLine(markdownLink, this.app);
Expand Down Expand Up @@ -127,14 +123,11 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
* if the capture path is invalid, or if the target folder is empty.
*/
private async getFormattedPathToCaptureTo(
shouldCaptureToActiveFile: boolean
shouldCaptureToActiveFile: boolean,
): Promise<string> {
if (shouldCaptureToActiveFile) {
const activeFile = this.app.workspace.getActiveFile();
invariant(
activeFile,
`Cannot capture to active file - no active file.`
);
invariant(activeFile, "Cannot capture to active file - no active file.");

return activeFile.path;
}
Expand All @@ -144,10 +137,7 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {

// Removing the trailing slash from the capture to path because otherwise isFolder will fail
// to get the folder.
const folderPath = formattedCaptureTo.replace(
/^\/$|\/\.md$|^\.md$/,
""
);
const folderPath = formattedCaptureTo.replace(/^\/$|\/\.md$|^\.md$/, "");
// Empty string means we suggest to capture anywhere in the vault.
const captureAnywhereInVault = folderPath === "";
const shouldCaptureToFolder =
Expand All @@ -168,29 +158,26 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {

private async selectFileInFolder(
folderPath: string,
captureAnywhereInVault: boolean
captureAnywhereInVault: boolean,
): Promise<string> {
const folderPathSlash =
folderPath.endsWith("/") || captureAnywhereInVault
? folderPath
: `${folderPath}/`;
const filesInFolder = getMarkdownFilesInFolder(folderPathSlash);

invariant(
filesInFolder.length > 0,
`Folder ${folderPathSlash} is empty.`
);
invariant(filesInFolder.length > 0, `Folder ${folderPathSlash} is empty.`);

const filePaths = filesInFolder.map((f) => f.path);
const targetFilePath = await InputSuggester.Suggest(
app,
filePaths.map((item) => item.replace(folderPathSlash, "")),
filePaths
filePaths,
);

invariant(
!!targetFilePath && targetFilePath.length > 0,
`No file selected for capture.`
"No file selected for capture.",
);

// Ensure user has selected a file in target folder. InputSuggester allows user to write
Expand All @@ -212,20 +199,20 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
const targetFilePath = await InputSuggester.Suggest(
app,
filePaths,
filePaths
filePaths,
);

invariant(
!!targetFilePath && targetFilePath.length > 0,
`No file selected for capture.`
"No file selected for capture.",
);

return await this.formatFilePath(targetFilePath);
}

private async onFileExists(
filePath: string,
content: string
content: string,
): Promise<{
file: TFile;
newFileContent: string;
Expand All @@ -245,7 +232,7 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
formatted,
this.choice,
fileContent,
file
file,
);

const secondReadFileContent: string = await this.app.vault.read(file);
Expand All @@ -255,14 +242,12 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
const res = merge(
secondReadFileContent,
fileContent,
formattedFileContent
formattedFileContent,
);
invariant(
!res.isSuccess,
() =>
`The file ${filePath} has been modified since the last read.` +
`\nQuickAdd could not merge the versions two without conflicts, and will not modify the file.` +
`\nThis is in order to prevent data loss.`
`The file ${filePath} has been modified since the last read.\nQuickAdd could not merge the versions two without conflicts, and will not modify the file.\nThis is in order to prevent data loss.`,
);

newFileContent = res.joinedResults() as string;
Expand All @@ -273,7 +258,7 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {

private async onCreateFileIfItDoesntExist(
filePath: string,
captureContent: string
captureContent: string,
): Promise<{
file: TFile;
newFileContent: string;
Expand All @@ -287,36 +272,30 @@ export class CaptureChoiceEngine extends QuickAddChoiceEngine {
this.app,
this.plugin,
this.choice.createFileIfItDoesntExist.template,
this.choiceExecutor
this.choiceExecutor,
);

fileContent = await singleTemplateEngine.run();
}

const file: TFile = await this.createFileWithInput(
filePath,
fileContent
);
const file: TFile = await this.createFileWithInput(filePath, fileContent);
await replaceTemplaterTemplatesInCreatedFile(this.app, file);

const updatedFileContent: string = await this.app.vault.cachedRead(
file
const updatedFileContent: string = await this.app.vault.cachedRead(file);
const newFileContent: string = await this.formatter.formatContentWithFile(
captureContent,
this.choice,
updatedFileContent,
file,
);
const newFileContent: string =
await this.formatter.formatContentWithFile(
captureContent,
this.choice,
updatedFileContent,
file
);

return { file, newFileContent, captureContent };
}

private async formatFilePath(captureTo: string) {
const formattedCaptureTo: string = await this.formatter.formatFileName(
captureTo,
this.choice.name
this.choice.name,
);

return this.normalizeMarkdownFilePath("", formattedCaptureTo);
Expand Down
54 changes: 47 additions & 7 deletions src/formatters/captureChoiceFormatter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { CompleteFormatter } from "./completeFormatter";
import type ICaptureChoice from "../types/choices/ICaptureChoice";
import type { App, TFile } from "obsidian";
import { MarkdownView, type TFile } from "obsidian";
import { log } from "../logger/logManager";
import type QuickAdd from "../main";
import type { IChoiceExecutor } from "../IChoiceExecutor";
import { templaterParseTemplate } from "../utilityObsidian";
import {
CREATE_IF_NOT_FOUND_BOTTOM,
CREATE_IF_NOT_FOUND_CURSOR,
CREATE_IF_NOT_FOUND_TOP,
} from "../constants";
import { escapeRegExp, getLinesInString } from "src/utility";
Expand All @@ -17,10 +16,6 @@ export class CaptureChoiceFormatter extends CompleteFormatter {
private file: TFile | null = null;
private fileContent = "";

constructor(app: App, plugin: QuickAdd, choiceExecutor: IChoiceExecutor) {
super(app, plugin, choiceExecutor);
}

public async formatContentWithFile(
input: string,
choice: ICaptureChoice,
Expand Down Expand Up @@ -160,6 +155,51 @@ export class CaptureChoiceFormatter extends CompleteFormatter {
) {
return `${this.fileContent}\n${insertAfterLineAndFormatted}`;
}

if (
this.choice.insertAfter?.createIfNotFoundLocation ===
CREATE_IF_NOT_FOUND_CURSOR
) {
try {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);

if (!activeView) {
throw new Error("No active view.");
}

const cursor = activeView.editor.getCursor();
let targetPosition = cursor.line;

if (this.choice.insertAfter?.insertAtEnd) {
if (!this.file)
throw new Error("Tried to get sections without file.");

const fileContentLines: string[] = getLinesInString(this.fileContent);

const endOfSectionIndex = getEndOfSection(
fileContentLines,
targetPosition,
!!this.choice.insertAfter.considerSubsections,
);

targetPosition = endOfSectionIndex ?? fileContentLines.length - 1;
}

const newFileContent = this.insertTextAfterPositionInBody(
insertAfterLineAndFormatted,
this.fileContent,
targetPosition,
);

return newFileContent;
} catch (e) {
log.logError(
`unable to insert line '${
this.choice.insertAfter.after
}' on your cursor.\n${e as string}`,
);
}
}
}

private getFrontmatterEndPosition(file: TFile) {
Expand Down
Loading

0 comments on commit f5d6e51

Please sign in to comment.