Skip to content

Commit 6fd5bff

Browse files
authored
feat: Intelligent dynamic auto-complete for entering tasks (#822)
* Autocomplete * Bug fixes * Added a configuration for a minimal match for opening the suggestor * Test coverage and commits needed to support that * Max suggestions as a setting * Move the end-of-line tag support to a different PR
1 parent 447edac commit 6fd5bff

File tree

9 files changed

+712
-30
lines changed

9 files changed

+712
-30
lines changed

src/Query/DateParser.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import * as chrono from 'chrono-node';
22

33
export class DateParser {
4-
public static parseDate(input: string): moment.Moment {
4+
public static parseDate(
5+
input: string,
6+
forwardDate: boolean = false,
7+
): moment.Moment {
58
// Using start of day to correctly match on comparison with other dates (like equality).
6-
return window.moment(chrono.parseDate(input)).startOf('day');
9+
return window
10+
.moment(
11+
chrono.parseDate(input, undefined, {
12+
forwardDate: forwardDate,
13+
}),
14+
)
15+
.startOf('day');
716
}
817
}

src/Suggestor/EditorSuggestorPopup.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import {
2+
App,
3+
Editor,
4+
EditorPosition,
5+
EditorSuggest,
6+
EditorSuggestContext,
7+
EditorSuggestTriggerInfo,
8+
TFile,
9+
} from 'obsidian';
10+
11+
import type { Settings } from '../config/Settings';
12+
import * as task from '../Task';
13+
import { SuggestInfo, buildSuggestions } from './Suggestor';
14+
15+
export type SuggestInfoWithContext = SuggestInfo & {
16+
context: EditorSuggestContext;
17+
};
18+
19+
export class EditorSuggestor extends EditorSuggest<SuggestInfoWithContext> {
20+
private settings: Settings;
21+
22+
constructor(app: App, settings: Settings) {
23+
super(app);
24+
this.settings = settings;
25+
}
26+
27+
onTrigger(
28+
cursor: EditorPosition,
29+
editor: Editor,
30+
_file: TFile,
31+
): EditorSuggestTriggerInfo | null {
32+
if (!this.settings.autoSuggestInEditor) return null;
33+
const line = editor.getLine(cursor.line);
34+
if (
35+
line.contains(this.settings.globalFilter) &&
36+
line.match(task.Task.taskRegex)
37+
) {
38+
return {
39+
start: { line: cursor.line, ch: 0 },
40+
end: {
41+
line: cursor.line,
42+
ch: line.length,
43+
},
44+
query: line,
45+
};
46+
}
47+
return null;
48+
}
49+
50+
getSuggestions(context: EditorSuggestContext): SuggestInfoWithContext[] {
51+
const line = context.query;
52+
const currentCursor = context.editor.getCursor();
53+
54+
const suggestions: SuggestInfo[] = buildSuggestions(
55+
line,
56+
currentCursor.ch,
57+
this.settings,
58+
);
59+
60+
// Add the editor context to all the suggestions
61+
const suggestionsWithContext: SuggestInfoWithContext[] = [];
62+
for (const suggestion of suggestions)
63+
suggestionsWithContext.push({ ...suggestion, context: context });
64+
65+
return suggestionsWithContext;
66+
}
67+
68+
renderSuggestion(value: SuggestInfoWithContext, el: HTMLElement) {
69+
el.setText(value.displayText);
70+
}
71+
72+
selectSuggestion(
73+
value: SuggestInfoWithContext,
74+
_evt: MouseEvent | KeyboardEvent,
75+
) {
76+
const editor = value.context.editor;
77+
if (value.suggestionType === 'empty') {
78+
// Close the suggestion dialog and simulate an Enter press to the editor
79+
this.close();
80+
const eventClone = new KeyboardEvent('keydown', {
81+
code: 'Enter',
82+
key: 'Enter',
83+
});
84+
(editor as any)?.cm?.contentDOM?.dispatchEvent(eventClone);
85+
return;
86+
}
87+
const currentCursor = value.context.editor.getCursor();
88+
const replaceFrom = {
89+
line: currentCursor.line,
90+
ch: value.insertAt ?? currentCursor.ch,
91+
};
92+
const replaceTo = value.insertSkip
93+
? {
94+
line: currentCursor.line,
95+
ch: replaceFrom.ch + value.insertSkip,
96+
}
97+
: undefined;
98+
value.context.editor.replaceRange(
99+
value.appendText,
100+
replaceFrom,
101+
replaceTo,
102+
);
103+
value.context.editor.setCursor({
104+
line: currentCursor.line,
105+
ch: replaceFrom.ch + value.appendText.length,
106+
});
107+
}
108+
}

0 commit comments

Comments
 (0)