|
| 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