Skip to content

Commit

Permalink
Improve to show suggestions more than 20 times faster (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
tadashi-aikawa committed Nov 27, 2021
1 parent 083a45b commit f6e047d
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 87 deletions.
35 changes: 35 additions & 0 deletions src/provider/CurrentFileWordProvider.ts
@@ -0,0 +1,35 @@
import { App, MarkdownView } from "obsidian";
import { groupBy, uniq } from "../util/collection-helper";
import { Word, WordsByFirstLetter } from "./suggester";
import { Tokenizer } from "../tokenizer/tokenizer";

export class CurrentFileWordProvider {
private words: Word[] = [];
wordsByFirstLetter: WordsByFirstLetter;

constructor(private app: App, private tokenizer: Tokenizer) {}

async refreshWords(): Promise<void> {
this.clearWords();

if (!this.app.workspace.getActiveViewOfType(MarkdownView)) {
return;
}

const file = this.app.workspace.getActiveFile();
if (!file) {
return;
}

const content = await this.app.vault.cachedRead(file);
this.words = uniq(this.tokenizer.tokenize(content)).map((x) => ({
value: x,
}));
this.wordsByFirstLetter = groupBy(this.words, (x) => x.value.charAt(0));
}

clearWords(): void {
this.words = [];
this.wordsByFirstLetter = {};
}
}
@@ -1,11 +1,6 @@
import { App, FileSystemAdapter, Notice } from "obsidian";
import { keyBy } from "./util/collection-helper";

export interface Word {
value: string;
description?: string;
aliases?: string[];
}
import { keyBy } from "../util/collection-helper";
import { pushWord, Word, WordsByFirstLetter } from "./suggester";

function lineToWord(line: string): Word {
const [value, description, ...aliases] = line.split("\t");
Expand All @@ -16,8 +11,10 @@ function lineToWord(line: string): Word {
};
}

export class CustomDictionaryService {
words: Word[] = [];
export class CustomDictionaryWordProvider {
private words: Word[] = [];
wordByValue: { [value: string]: Word };
wordsByFirstLetter: WordsByFirstLetter;

private app: App;
private fileSystemAdapter: FileSystemAdapter;
Expand All @@ -29,23 +26,19 @@ export class CustomDictionaryService {
this.paths = paths;
}

get wordsByValue(): { [value: string]: Word } {
return keyBy(this.words, (x) => x.value);
}

updatePaths(paths: string[]): void {
this.paths = paths;
}

async loadWords(path: string): Promise<Word[]> {
return (await this.fileSystemAdapter.read(path))
.split(/(\r\n|\n)/)
.split(/\r\n|\n/)
.filter((x) => x)
.map(lineToWord);
}

async refreshCustomTokens(): Promise<void> {
this.clearTokens();
async refreshCustomWords(): Promise<void> {
this.clearWords();

for (const path of this.paths) {
try {
Expand All @@ -59,9 +52,19 @@ export class CustomDictionaryService {
);
}
}

this.wordByValue = keyBy(this.words, (x) => x.value);
for (const word of this.words) {
pushWord(this.wordsByFirstLetter, word.value.charAt(0), word);
word.aliases?.forEach((a) =>
pushWord(this.wordsByFirstLetter, a.charAt(0), word)
);
}
}

clearTokens(): void {
clearWords(): void {
this.words = [];
this.wordByValue = {};
this.wordsByFirstLetter = {};
}
}
44 changes: 44 additions & 0 deletions src/provider/InternalLinkWordProvider.ts
@@ -0,0 +1,44 @@
import { App } from "obsidian";
import { pushWord, Word, WordsByFirstLetter } from "./suggester";
import { AppHelper } from "../app-helper";

export class InternalLinkWordProvider {
private words: Word[] = [];
wordsByFirstLetter: WordsByFirstLetter;

constructor(private app: App, private appHelper: AppHelper) {}

refreshWords(): void {
this.clearWords();

const resolvedInternalLinkWords = this.app.vault
.getMarkdownFiles()
.map((x) => ({
value: `[[${x.basename}]]`,
aliases: [x.basename, ...this.appHelper.getAliases(x)],
description: x.path,
}));

const unresolvedInternalLinkWords = this.appHelper
.searchPhantomLinks()
.map((x) => ({
value: `[[${x}]]`,
aliases: [x],
description: "Not created yet",
}));

this.words = [...resolvedInternalLinkWords, ...unresolvedInternalLinkWords];
for (const word of this.words) {
// 2 because `[[..`
pushWord(this.wordsByFirstLetter, word.value.charAt(2), word);
word.aliases?.forEach((a) =>
pushWord(this.wordsByFirstLetter, a.charAt(2), word)
);
}
}

clearWords(): void {
this.words = [];
this.wordsByFirstLetter = {};
}
}
54 changes: 46 additions & 8 deletions src/suggester/suggester.ts → src/provider/suggester.ts
@@ -1,17 +1,38 @@
import { Word } from "../CustomDictionaryService";
import {
capitalizeFirstLetter,
lowerStartsWith,
lowerStartsWithoutSpace,
startsWithoutSpace,
} from "../util/strings";
import { IndexedWords } from "../ui/AutoCompleteSuggest";

export interface Word {
value: string;
description?: string;
aliases?: string[];
}

export type WordsByFirstLetter = { [firstLetter: string]: Word[] };

interface Judgement {
word: Word;
value?: string;
alias: boolean;
}

export function pushWord(
wordsByFirstLetter: WordsByFirstLetter,
key: string,
word: Word
) {
if (wordsByFirstLetter[key] === undefined) {
wordsByFirstLetter[key] = [word];
return;
}

wordsByFirstLetter[key].push(word);
}

function judge(
word: Word,
query: string,
Expand All @@ -22,18 +43,18 @@ function judge(
}

if (
word.value.startsWith("[[")
? lowerStartsWithoutSpace(word.value.replace("[[", ""), query)
: startsWithoutSpace(word.value, query)
queryStartWithUpper &&
startsWithoutSpace(capitalizeFirstLetter(word.value), query)
) {
word.value = capitalizeFirstLetter(word.value);
return { word: word, value: word.value, alias: false };
}

if (
queryStartWithUpper &&
startsWithoutSpace(capitalizeFirstLetter(word.value), query)
word.value.startsWith("[[")
? lowerStartsWithoutSpace(word.value.replace("[[", ""), query)
: lowerStartsWithoutSpace(word.value, query)
) {
word.value = capitalizeFirstLetter(word.value);
return { word: word, value: word.value, alias: false };
}

Expand All @@ -48,11 +69,28 @@ function judge(
}

export function suggestWords(
words: Word[],
indexedWords: IndexedWords,
query: string,
max: number
): Word[] {
const queryStartWithUpper = capitalizeFirstLetter(query) === query;

const words = queryStartWithUpper
? [
...(indexedWords.currentFile[query.charAt(0)] ?? []),
...(indexedWords.currentFile[query.charAt(0).toLowerCase()] ?? []),
...(indexedWords.customDictionary[query.charAt(0)] ?? []),
...(indexedWords.customDictionary[query.charAt(0).toLowerCase()] ?? []),
...(indexedWords.internalLink[query.charAt(0)] ?? []),
...(indexedWords.internalLink[query.charAt(0).toLowerCase()] ?? []),
]
: [
...(indexedWords.currentFile[query.charAt(0)] ?? []),
...(indexedWords.customDictionary[query.charAt(0)] ?? []),
...(indexedWords.internalLink[query.charAt(0)] ?? []),
...(indexedWords.internalLink[query.charAt(0).toUpperCase()] ?? []),
];

return Array.from(words)
.map((x) => judge(x, query, queryStartWithUpper))
.filter((x) => x.value !== undefined)
Expand Down

0 comments on commit f6e047d

Please sign in to comment.