Skip to content

Commit

Permalink
Merge pull request #40 from kortina/ak-perf-improvements
Browse files Browse the repository at this point in the history
cache results of parsing files in workspace for tags and wiki-links
  • Loading branch information
kortina authored Jul 1, 2020
2 parents 0e4ede6 + 3bad813 commit 4af718e
Show file tree
Hide file tree
Showing 18 changed files with 453 additions and 431 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## [v0.0.10](https://github.com/kortina/vscode-markdown-notes/releases/edit/v0.0.10) (2020-06-30)

Major refactor that should dramatically improve performance by caching results of parsing files in workspace for `[[wiki-links]]` and `#tags` (instead of re-parsing just-in-time, every time we want suggestions.)

**Enhancements:**

- cache results of parsing files in workspace. Closes #31
- support for `mdx` and `fountain` files and new config `vscodeMarkdownNotes.defaultFileExtension` allows you to set the extension that gets appended upon note creation. Closes #44 /ht @andermerwed

**Fixes:**

- newly created tags will be registered for auto-completion (once they file they are in is saved to disk). Closes #36 /ht @b3u
- line numbers are no longer off by one. Closes #35 /ht @b3u

**Cleanup:**

- remove unused `RemarkParser` stuff ( left in branch https://github.com/kortina/vscode-markdown-notes/tree/ak-remark-parser )
- renames for clarity:
- `ReferenceSearch` to `NoteParser`
- `ContextWord` to `Ref`
- cleanup / dry-up errant references to file extension match regexes, now at `_rxFileExtensions`

## [v0.0.9](https://github.com/kortina/vscode-markdown-notes/releases/edit/v0.0.9) (2020-05-31)

Major Refactor with regexes allowing wiki links w spaces and w/o extensions. Basic backlinks.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# VS Code Markdown Notes
# Markdown Notes for VS Code

Use `[[wiki-links]]`, `backlinks`, and `#tags` for fast-navigation of markdown notes.

Expand All @@ -12,7 +12,7 @@ Bring some of the awesome features from apps like [Notational Velocity](http://n

A popular feature in [Roam Research](https://roamresearch.com/) and [Bear](https://bear.app/) is the ability to quickly reference other notes using "Cross-Note Links" in the `[[wiki-link]]` style.

VS Code Markdown notes provides syntax highlighting, auto-complete, Go to Definition (`editor.action.revealDefinition`), and Peek Definition (`editor.action.peekDefinition`) support for wiki-links to notes in a workspace.
**Markdown Notes** provides syntax highlighting, auto-complete, Go to Definition (`editor.action.revealDefinition`), and Peek Definition (`editor.action.peekDefinition`) support for wiki-links to notes in a workspace.

By default, the extension assumes each markdown file in a workspace has a unique name, so that `note.md` will resolve to the file with this name, regardless of whether or not this file exists in any subdirectory path. This tends to be a bit cleaner, but if you want support for multiple files with the same name, in `settings.json` set `"vscodeMarkdownNotes.workspaceFilenameConvention": "relativePaths"`, and you'll get completions like `note1/note.md` and `../note2/note.md`.

Expand Down
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "vscode-markdown-notes",
"displayName": "VS Code Markdown Notes",
"displayName": "Markdown Notes",
"description": "Navigate notes with [[wiki-links]], backlinks, and #tags (like Bear, Roam, etc). Automatically create notes from new inline [[wiki-links]]. Use Peek Definition to preview linked notes.",
"version": "0.0.9",
"version": "0.0.10",
"publisher": "kortina",
"repository": {
"url": "https://github.com/kortina/vscode-markdown-notes.git",
Expand Down Expand Up @@ -44,11 +44,11 @@
"commands": [
{
"command": "vscodeMarkdownNotes.newNote",
"title": "VS Code Markdown Notes: New Note"
"title": "Markdown Notes: New Note"
}
],
"configuration": {
"title": "VS Code Markdown Notes Configuration",
"title": "Markdown Notes Configuration",
"properties": {
"vscodeMarkdownNotes.noteCompletionConvention": {
"type": "string",
Expand Down Expand Up @@ -79,6 +79,11 @@
"default": "-",
"description": "When creating new notes from a 'Title Case Note Name', slugify non-word characters with '-' (default) or '_', or don't slugify non-word characters by setting to 'NONE.'"
},
"vscodeMarkdownNotes.defaultFileExtension": {
"type": "string",
"default": "md",
"description": "When creating new notes from a 'Title Case Note Name', append this extension to the filename. Defaults to 'md'"
},
"vscodeMarkdownNotes.createNoteOnGoToDefinitionWhenMissing": {
"type": "boolean",
"default": true,
Expand Down
10 changes: 7 additions & 3 deletions src/BacklinksTreeDataProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import { ReferenceSearch } from './ReferenceSearch';
import { NoteParser } from './NoteParser';

type FileWithLocations = {
file: string;
Expand Down Expand Up @@ -81,7 +81,11 @@ export class BacklinksTreeDataProvider implements vscode.TreeDataProvider<Backli

getChildren(element?: BacklinkItem): Thenable<BacklinkItem[]> {
let f = vscode.window.activeTextEditor?.document.uri.fsPath;
if (!this.workspaceRoot || !f) {
if (!f) {
// no activeTextEditor, so there can be no refs
return Promise.resolve([]);
}
if (!this.workspaceRoot) {
vscode.window.showInformationMessage('No refs in empty workspace');
return Promise.resolve([]);
}
Expand All @@ -91,7 +95,7 @@ export class BacklinksTreeDataProvider implements vscode.TreeDataProvider<Backli
// Parse the workspace into list of FilesWithLocations
// Return 1 collapsible element per file
if (!element) {
return ReferenceSearch.searchBacklinksFor(activeFilename).then((locations) => {
return NoteParser.searchBacklinksFor(activeFilename).then((locations) => {
let filesWithLocations = BacklinksTreeDataProvider.locationListToTree(locations);
return filesWithLocations.map((fwl) => BacklinkItem.fromFileWithLocations(fwl));
});
Expand Down
32 changes: 15 additions & 17 deletions src/MarkdownDefinitionProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as vscode from 'vscode';
import { ContextWord, ContextWordType, getContextWord } from './ContextWord';
import { Ref, RefType, getRefAt } from './Ref';
import { NoteWorkspace } from './NoteWorkspace';
import { basename, dirname, join, resolve } from 'path';
import { existsSync, writeFileSync } from 'fs';
Expand Down Expand Up @@ -27,16 +27,16 @@ export class MarkdownDefinitionProvider implements vscode.DefinitionProvider {
//
// console.debug('provideDefinition');

const contextWord = getContextWord(document, position);
// debugContextWord(contextWord);
if (contextWord.type != ContextWordType.WikiLink) {
// console.debug('getContextWord was not WikiLink');
const ref = getRefAt(document, position);
// debugRef(ref);
if (ref.type != RefType.WikiLink) {
// console.debug('getRefAt was not WikiLink');
return [];
}

// TODO: parameterize extensions. return if we don't have a filename and we require extensions
// const markdownFileRegex = /[\w\.\-\_\/\\]+\.(md|markdown)/i;
const selectedWord = contextWord.word;
const selectedWord = ref.word;
// console.debug('selectedWord', selectedWord);
let files: Array<vscode.Uri> = [];
// selectedWord might be either:
Expand All @@ -48,8 +48,9 @@ export class MarkdownDefinitionProvider implements vscode.DefinitionProvider {
// However, only check for basenames in the entire project if:
if (NoteWorkspace.useUniqueFilenames()) {
// there should be exactly 1 file with name = selectedWord
files = (await vscode.workspace.findFiles('**/*')).filter((f) => {
return NoteWorkspace.noteNamesFuzzyMatch(f.fsPath, contextWord.word);
files = (await NoteWorkspace.noteFiles()).filter((f) => {
// files = (await vscode.workspace.findFiles('**/*')).filter((f) => {
return NoteWorkspace.noteNamesFuzzyMatch(f.fsPath, ref.word);
});
}
// If we did not find any files in the workspace,
Expand All @@ -66,7 +67,7 @@ export class MarkdownDefinitionProvider implements vscode.DefinitionProvider {

// else, create the file
if (files.length == 0) {
const path = MarkdownDefinitionProvider.createMissingNote(contextWord);
const path = MarkdownDefinitionProvider.createMissingNote(ref);
if (path !== undefined) {
files.push(vscode.Uri.file(path));
}
Expand All @@ -77,9 +78,9 @@ export class MarkdownDefinitionProvider implements vscode.DefinitionProvider {
}

// FIXME: move all of the stuff that deals with create the filename to NoteWorkspace
static createMissingNote = (contextWord: ContextWord): string | undefined => {
// don't create new files if contextWord is a Tag
if (contextWord.type != ContextWordType.WikiLink) {
static createMissingNote = (ref: Ref): string | undefined => {
// don't create new files if ref is a Tag
if (ref.type != RefType.WikiLink) {
return;
}
if (!NoteWorkspace.createNoteOnGoToDefinitionWhenMissing()) {
Expand All @@ -93,14 +94,11 @@ export class MarkdownDefinitionProvider implements vscode.DefinitionProvider {
);
return;
}
// add an extension if one does not exist
let mdFilename = contextWord.word.match(/\.(md|markdown)$/i)
? contextWord.word
: `${contextWord.word}.md`;
let mdFilename = NoteWorkspace.noteFileNameFromTitle(ref.word);
// by default, create new note in same dir as the current document
// TODO: could convert this to an option (to, eg, create in workspace root)
const path = join(dirname(filename), mdFilename);
const title = titleCaseFilename(contextWord.word);
const title = titleCaseFilename(ref.word);
writeFileSync(path, `# ${title}\n\n`);
return path;
}
Expand Down
39 changes: 13 additions & 26 deletions src/MarkdownFileCompletionItemProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as vscode from 'vscode';
import { ContextWordType, getContextWord } from './ContextWord';
import { WorkspaceTagList } from './WorkspaceTagList';
import { RefType, getRefAt } from './Ref';
import { NoteWorkspace } from './NoteWorkspace';
import { NoteParser, Note } from './NoteParser';

// Given a document and position, check whether the current word matches one of
// these 2 contexts:
Expand All @@ -16,45 +16,32 @@ export class MarkdownFileCompletionItemProvider implements vscode.CompletionItem
_token: vscode.CancellationToken,
context: vscode.CompletionContext
) {
const contextWord = getContextWord(document, position);
// console.debug(
// `contextWord: '${contextWord.word}' start: (${contextWord.range?.start.line}, ${contextWord.range?.start.character}) end: (${contextWord.range?.end.line}, ${contextWord.range?.end.character}) context: (${position.line}, ${position.character})`
// );
// console.debug(`provideCompletionItems ${ContextWordType[contextWord.type]}`);
const ref = getRefAt(document, position);
let items = [];
switch (contextWord.type) {
case ContextWordType.Null:
switch (ref.type) {
case RefType.Null:
return [];
break;
case ContextWordType.Tag:
// console.debug(`ContextWordType.Tag`);
// console.debug(
// `contextWord.word: ${contextWord.word} TAG_WORD_SET: ${Array.from(
// WorkspaceTagList.TAG_WORD_SET
// )}`
// );
items = Array.from(WorkspaceTagList.TAG_WORD_SET).map((t) => {
case RefType.Tag:
items = (await NoteParser.distinctTags()).map((t) => {
let kind = vscode.CompletionItemKind.File;
let label = `${t}`; // cast to a string
let item = new vscode.CompletionItem(label, kind);
if (contextWord && contextWord.range) {
item.range = contextWord.range;
if (ref && ref.range) {
item.range = ref.range;
}
return item;
});
return items;
break;
case ContextWordType.WikiLink:
let files = (await vscode.workspace.findFiles('**/*')).filter(
// TODO: parameterize extensions. Add $ to end?
(f) => f.scheme == 'file' && f.path.match(/\.(md|markdown)/i)
);
case RefType.WikiLink:
let files = await NoteWorkspace.noteFiles();
items = files.map((f) => {
let kind = vscode.CompletionItemKind.File;
let label = NoteWorkspace.wikiLinkCompletionForConvention(f, document);
let item = new vscode.CompletionItem(label, kind);
if (contextWord && contextWord.range) {
item.range = contextWord.range;
if (ref && ref.range) {
item.range = ref.range;
}
return item;
});
Expand Down
10 changes: 5 additions & 5 deletions src/MarkdownReferenceProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
import { ReferenceSearch } from './ReferenceSearch';
import { getContextWord } from './ContextWord';
import { NoteParser } from './NoteParser';
import { getRefAt } from './Ref';

export class MarkdownReferenceProvider implements vscode.ReferenceProvider {
public provideReferences(
Expand All @@ -10,8 +10,8 @@ export class MarkdownReferenceProvider implements vscode.ReferenceProvider {
token: vscode.CancellationToken
): vscode.ProviderResult<vscode.Location[]> {
// console.debug('MarkdownReferenceProvider.provideReferences');
const contextWord = getContextWord(document, position);
// debugContextWord(contextWord);
return ReferenceSearch.search(contextWord);
const ref = getRefAt(document, position);
// debugRef(ref);
return NoteParser.search(ref);
}
}
Loading

0 comments on commit 4af718e

Please sign in to comment.