Skip to content

Commit

Permalink
Make the typescript compiler happy (#1)
Browse files Browse the repository at this point in the history
* Fix all the TSC errors that have been hidden

* Fix consistency

* Fix typo

* More cleanup with the help of tsserver
  • Loading branch information
marcaddeo authored Aug 31, 2024
1 parent 74ac76b commit 63dd0ee
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 60 deletions.
5 changes: 2 additions & 3 deletions src/Api/TaskMoverApiV1.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { App, TFile } from 'obsidian';
import { MarkdownView } from 'obsidian';
import { type TFile, MarkdownView } from 'obsidian';

/**
* Task Mover API v1 interface
Expand All @@ -12,5 +11,5 @@ export interface TaskMoverApiV1 {
* @param view The current markdown MarkdownView
* @param destination The destination file to move the task to.
*/
moveTaskToNote: async (view: MarkdownView, destination: TFile);
moveTaskToNote(view: MarkdownView, destination: TFile): Promise<void>;
}
3 changes: 1 addition & 2 deletions src/Api/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { App, TFile } from 'obsidian';
import { MarkdownView } from 'obsidian';
import { type App, type TFile, MarkdownView } from 'obsidian';
import type { TaskMoverApiV1 } from './TaskMoverApiV1';
import { moveTaskToNote } from './moveTaskToNote'

Expand Down
47 changes: 31 additions & 16 deletions src/Api/moveTaskToNote.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import type { App, TFile } from 'obsidian';
import { normalizePath, MarkdownView, Notice } from 'obsidian';
import { type App, type TFile, normalizePath, MarkdownView, Notice } from 'obsidian';
import { customAlphabet } from 'nanoid';
import type { Task } from '../types';

declare module 'obsidian' {
interface App {
plugins: {
plugins: Record<string, {
getTasks: () => Task[];
}>
};
}
}

/**
* Convert a task to file line string, including its child tasks.
*
* @param task The task to convert to file line strings
*/
function* taskToFileLineStringWithChildren(task: object): Generator<string> {
function* taskToFileLineStringWithChildren(task: Task): Generator<string> {
yield task.toFileLineString();

for (const child: object of task.children) {
for (const child of task.children) {
yield* taskToFileLineStringWithChildren(child);
}
}
Expand All @@ -23,12 +33,14 @@ function* taskToFileLineStringWithChildren(task: object): Generator<string> {
*
* @return The task under the cursor, or null if there is not one.
*/
export const getTaskUnderCursor = function (app: App, view: MarkdownView): object | null {
const activeFilePath: string = view.getFile().path;
export const getTaskUnderCursor = (app: App, view: MarkdownView): Task | null => {
const activeFilePath = view.file?.path;
if (!activeFilePath) return null;

const lineNumber: number = view.editor.getCursor().line;

// Find the current task under the cursor.
const tasks: Array<object> = app.plugins.plugins['obsidian-tasks-plugin'].getTasks();
const tasks: Array<Task> = app.plugins.plugins['obsidian-tasks-plugin'].getTasks();
return tasks[tasks.findIndex(t => t.file.path === activeFilePath && t.lineNumber === lineNumber)] ?? null;
}

Expand All @@ -45,9 +57,15 @@ export const moveTaskToNote = async (app: App, view: MarkdownView, destination:
new Notice('Error finding task on current line');
return;
}

const normalizedPath: string = await normalizePath(destination.path);
const blockLinkRef: string = customAlphabet('abcdefghijklmnopqrstuvwz0123456789', 6)();

const normalizedPath = normalizePath(destination.path);
const destinationFile = app.vault.getFileByPath(normalizedPath);
if (!destinationFile) {
new Notice('There was an error getting the destination note path')
return;
}

const blockLinkRef = customAlphabet('abcdefghijklmnopqrstuvwz0123456789', 6)();
// Append the block link reference onto the original task.
task.blockLink = ` ^${blockLinkRef}`;

Expand All @@ -57,20 +75,17 @@ export const moveTaskToNote = async (app: App, view: MarkdownView, destination:
taskStrings = taskStrings.map(taskString => taskString.replace(task.indentation, ''));

// Append task(s) to destination file.
await app.vault.append(
app.vault.getFileByPath(normalizedPath),
['', ...taskStrings].join('\n')
);
await app.vault.append(destinationFile, ['', ...taskStrings].join('\n'));

// Construct a block link to the parent task in the destination file.
const linktext: string = app.metadataCache.fileToLinktext(destination, normalizedPath);
const blockLink: string = `${task.indentation}${task.listMarker} [[${linktext}#^${blockLinkRef}|${task.description}]]\n`;
const blockLink = `${task.indentation}${task.listMarker} [[${linktext}#^${blockLinkRef}|${task.description}]]\n`;

// Replace tasks(s) on current line with the block link to the task(s) in the destination file.
const lineNumber: number = view.editor.getCursor().line;
view.editor.replaceRange(
blockLink,
{ line: lineNumber, ch: 0 },
{ line: (task.children.length ? task.children.at(-1).lineNumber : lineNumber) + 1, ch: 0 }
{ line: (task.children.length ? (task.children.at(-1) as Task).lineNumber : lineNumber) + 1, ch: 0 }
);
};
22 changes: 12 additions & 10 deletions src/Settings/TaskMoverSettingsTab.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { debounce, App, PluginSettingTab, Setting } from 'obsidian';
import { TaskMoverPlugin } from '../main';
import { debounce, App, PluginSettingTab, Setting, ButtonComponent } from 'obsidian';
import TaskMoverPlugin from '../main';
import { FileSuggest } from '../ui/FileSuggest';
import type { DestinationNote } from '../types';

export class TaskMoverSettingsTab extends PluginSettingTab {
plugin: TaskMoverPlugin;
Expand All @@ -17,7 +18,7 @@ export class TaskMoverSettingsTab extends PluginSettingTab {
this.containerEl.addClass('task-mover-settings');

containerEl.createEl('p', {
text: 'Task Mover will add commands to the command palette, and optionally the editor context menu, to move tasks to each destination note entered below.' ,
text: 'Task Mover will add commands to the command palette, and optionally the editor context menu, to move tasks to each destination note entered below.',
});
containerEl.createEl('p', {
text: 'When a Task Mover command is executed, the task currently under the cursor and any child tasks will be moved to the bottom of the selected Destination Note.',
Expand All @@ -38,7 +39,7 @@ export class TaskMoverSettingsTab extends PluginSettingTab {
this.plugin.settings.destinationNotes.push({} as DestinationNote);
await this.plugin.saveSettings();
return this.display();
})
});
});

this.plugin.settings.destinationNotes.forEach((destination, index) => {
Expand All @@ -50,14 +51,16 @@ export class TaskMoverSettingsTab extends PluginSettingTab {
.setValue(destination.path)
.onChange(async (path: string) => {
const original: DestinationNote = this.plugin.settings.destinationNotes[index];
const name: string = original.name?.length ? original.name : app.vault.getFileByPath(path).basename;
const name = original.name?.length ? original.name : this.app.vault.getFileByPath(path)?.basename;

if (!name) throw new Error('Could not determine destination note name.');

this.plugin.settings.destinationNotes[index] =
{ ...original, ...{ path: path, name: name } };

await this.plugin.saveSettings();
return this.display();
})
});
})
.addText((text) => {
text
Expand All @@ -67,10 +70,10 @@ export class TaskMoverSettingsTab extends PluginSettingTab {
const original: DestinationNote = this.plugin.settings.destinationNotes[index];

this.plugin.settings.destinationNotes[index] =
{ ...original, ...{ name: name } };
{ ...original, ...{ name: name } };

await this.plugin.saveSettings();
}), 250, true)
}, 250, true));
})
.addToggle((toggle) => {
toggle
Expand All @@ -79,8 +82,7 @@ export class TaskMoverSettingsTab extends PluginSettingTab {
.onChange(async (showInEditorContextMenu) => {
this.plugin.settings.destinationNotes[index].showInEditorContextMenu = showInEditorContextMenu;
await this.plugin.saveSettings();
})
;
});
})
.addExtraButton((button) => {
button
Expand Down
24 changes: 14 additions & 10 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Editor, MarkdownView, Plugin, TFile } from 'obsidian';
import GithubSlugger from 'github-slugger'
import { TaskMoverPluginSettings } from './Settings/settings';
import { TaskMoverSettingsTab } from './Settings/TaskMoverSettingsTab'
import { DEFAULT_SETTINGS, type DestinationNote } from 'types';
import { DEFAULT_SETTINGS, type DestinationNote, type TaskMoverPluginSettings } from 'types';
import { taskMoverApiV1 } from './Api';
import { TaskMoverApiV1 } from './Api/TaskMoverApiV1';
import { getTaskUnderCursor } from './Api/moveTaskToNote';
Expand All @@ -22,15 +21,15 @@ export default class TaskMoverPlugin extends Plugin {
this.slugger = new GithubSlugger();

// Add a 'Move Task to ___' command for each destination note.
this.settings.destinationNotes.forEach((destination: DestinationNote, index: Number) => {
this.settings.destinationNotes.forEach((destination: DestinationNote) => {
const slug = this.slugger.slug(destination.name);

this.addCommand({
id: `move-task-to-${slug}`,
name: `Move task to ${destination.name} (MTT ${destination.name})`,
editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView): boolean => {
editorCheckCallback: (checking: boolean, _: Editor, view: MarkdownView): boolean => {
const task = getTaskUnderCursor(this.app, view);

if (task) {
if (!checking) {
this.moveTaskToNoteDestination(destination, view);
Expand All @@ -47,7 +46,7 @@ export default class TaskMoverPlugin extends Plugin {
this.addCommand({
id: 'move-task-to-file',
name: 'Move task to ... (MTTF)',
editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView): boolean => {
editorCheckCallback: (checking: boolean, _: Editor, view: MarkdownView): boolean => {
if (getTaskUnderCursor(this.app, view)) {
if (!checking) {
this.moveTaskToNoteWithFuzzySuggester(view);
Expand All @@ -61,9 +60,10 @@ export default class TaskMoverPlugin extends Plugin {
});

this.registerEvent(this.app.workspace.on('editor-menu', (menu) => {
const view: MarkdownView = this.app.workspace
const view = this.app.workspace
.getActiveViewOfType(MarkdownView);

if (!view) return;
if (!getTaskUnderCursor(this.app, view)) return;

const destinations = this.settings.destinationNotes
Expand Down Expand Up @@ -118,6 +118,8 @@ export default class TaskMoverPlugin extends Plugin {
*/
private moveTaskToNoteWithFuzzySuggester(view: MarkdownView) {
const currentFile = this.app.workspace.getActiveFile();
if (!currentFile) return;

const files: TFile[] = this.app.vault.getMarkdownFiles();
GenericSuggester.Suggest(
this.app,
Expand All @@ -130,8 +132,8 @@ export default class TaskMoverPlugin extends Plugin {
}),
files,
)
.then(file => this.apiV1.moveTaskToNote(view, file))
.catch(e => {});
.then(file => this.apiV1.moveTaskToNote(view, file))
.catch(_ => { });
}

/**
Expand All @@ -141,8 +143,10 @@ export default class TaskMoverPlugin extends Plugin {
* @param view The current markdown view
*/
private moveTaskToNoteDestination(destination: DestinationNote, view: MarkdownView) {
const file: TFile = this.app.vault
const file = this.app.vault
.getFileByPath(destination.path);
if (!file) return;

this.apiV1.moveTaskToNote(view, file);
}
}
14 changes: 14 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { TFile } from 'obsidian';

export interface DestinationNote {
path: string;
name: string;
Expand All @@ -11,3 +13,15 @@ export interface TaskMoverPluginSettings {
export const DEFAULT_SETTINGS: TaskMoverPluginSettings = {
destinationNotes: [],
}

// An incomplete representation of a Task from the Tasks plugin.
export interface Task {
toFileLineString: () => string;
children: Task[];
file: TFile;
lineNumber: number;
blockLink: string;
indentation: string;
listMarker: string;
description: string;
}
6 changes: 3 additions & 3 deletions src/ui/FileSuggest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @see https://github.com/liamcain/obsidian-periodic-notes/blob/main/src/ui/file-suggest.ts
import { TAbstractFile, TFile } from "obsidian";
import { TAbstractFile, TFile } from 'obsidian';
import { TextInputSuggest } from "./TextInputSuggest";

export class FileSuggest extends TextInputSuggest<TFile> {
Expand All @@ -11,7 +11,7 @@ export class FileSuggest extends TextInputSuggest<TFile> {
abstractFiles.forEach((file: TAbstractFile) => {
if (
file instanceof TFile &&
file.extension === "md" &&
file.extension === 'md' &&
file.path.toLowerCase().contains(lowerCaseInputStr)
) {
files.push(file);
Expand All @@ -27,7 +27,7 @@ export class FileSuggest extends TextInputSuggest<TFile> {

selectSuggestion(file: TFile): void {
this.inputEl.value = file.path;
this.inputEl.trigger("input");
this.inputEl.trigger('input');
this.close();
}
}
36 changes: 20 additions & 16 deletions src/ui/GenericSuggester.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
// https://raw.githubusercontent.com/chhoumann/quickadd/master/src/gui/GenericSuggester/genericSuggester.ts
import { FuzzySuggestModal } from "obsidian";
import type { FuzzyMatch , App} from "obsidian";
// @see https://raw.githubusercontent.com/chhoumann/quickadd/master/src/gui/GenericSuggester/genericSuggester.ts
import { type FuzzyMatch, type App, FuzzySuggestModal } from 'obsidian';

declare module 'obsidian' {
interface FuzzySuggestModal<T> {
chooser: {
values: {
item: string;
match: { score: number; matches: unknown[]; };
}[];
selectedItem: number;
[key: string]: unknown;
}
}
}

export default class GenericSuggester<T> extends FuzzySuggestModal<T> {
private resolvePromise: (value: T) => void;
Expand All @@ -25,21 +37,13 @@ export default class GenericSuggester<T> extends FuzzySuggestModal<T> {
this.rejectPromise = reject;
});

this.inputEl.addEventListener("keydown", (event: KeyboardEvent) => {
this.inputEl.addEventListener('keydown', (event: KeyboardEvent) => {
// chooser is undocumented & not officially a part of the Obsidian API, hence the precautions in using it.
if (event.code !== "Tab" || !("chooser" in this)) {
if (event.code !== 'Tab' || !('chooser' in this)) {
return;
}

const { values, selectedItem } = this.chooser as {
values: {
item: string;
match: { score: number; matches: unknown[]; };
}[];
selectedItem: number;
[key: string]: unknown;
};

const { values, selectedItem } = this.chooser;
const { value } = this.inputEl;
this.inputEl.value = values[selectedItem].item ?? value;
});
Expand All @@ -63,14 +67,14 @@ export default class GenericSuggester<T> extends FuzzySuggestModal<T> {
super.selectSuggestion(value, evt);
}

onChooseItem(item: T, evt: MouseEvent | KeyboardEvent): void {
onChooseItem(item: T, _: MouseEvent | KeyboardEvent): void {
this.resolved = true;
this.resolvePromise(item);
}

onClose() {
super.onClose();

if (!this.resolved) this.rejectPromise("no input given.");
if (!this.resolved) this.rejectPromise('no input given.');
}
}

0 comments on commit 63dd0ee

Please sign in to comment.