Skip to content

Commit

Permalink
Merge pull request #18 from kortina/ak-calculate-range-for-completion…
Browse files Browse the repository at this point in the history
…-items

calculate range for CompletionItem
  • Loading branch information
kortina authored May 12, 2020
2 parents aa8d7f3 + eaeeef7 commit 091df42
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 55 deletions.
17 changes: 17 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"cSpell.words": [
"Backlinks",
"Devops",
"Suping",
"basenames",
"filepath",
"jumplist",
"notetaking",
"nvalt",
"prefill",
"vpackage",
"vpublish",
"vsce",
"vsix"
]
}
23 changes: 10 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# VS Code Markdown Notes

Use `[[wiki-links]]` and `#tags` for fast-navigation between notes kept in in a VS Code workspace. Quickly create new notes from a Tilte Case Note Name.
Use `[[wiki-links]]` and `#tags` for fast-navigation between notes kept in in a VS Code workspace. Quickly create new notes from a Title Case Note Name.

There are many great note-taking applications ([Notational Velocity](http://notational.net/), [nvalt](https://brettterpstra.com/projects/nvalt/), [Bear](https://bear.app/), [FSNotes](https://fsnot.es/)), but few of them offer the extensibility of VS Code and the ability to use Vim bindings for editing notes.

Expand Down Expand Up @@ -28,7 +28,7 @@ You can bind this to a keyboard shortcut by adding to your `keybindings.json`:

```json
{
"key": "al+shiftt+n",
"key": "al+shift+n",
"command": "vscodeMarkdownNotes.newNote",
},
```
Expand Down Expand Up @@ -66,19 +66,17 @@ Run `npm install` first.
### FAQ

- "Autocomplete / Intellisense is not working - why?"
- Make sure that quick suggestions are enabled in Markdown. Put this in settings.json:

```
"[markdown]": {
"editor.quickSuggestions": true
}
```
- Quick suggestions are not enabled by default in Markdown, so you have to manually `triggerSuggest` OR put this in settings.json:
```
"[markdown]": {
"editor.quickSuggestions": true
}
```
- "New note is not working - why?"
- New Note works only when you are in a workspace. Look [here](https://stackoverflow.com/questions/44629890/what-is-a-workspace-in-visual-studio-code) for more information on workspaces in VS Code.
- New Note works only when you are in a workspace. Look [here](https://stackoverflow.com/questions/44629890/what-is-a-workspace-in-visual-studio-code) for more information on workspaces in VS Code.

### Known Issues

- Filename completion seems to be triggering when not in the `[[` context.
- The `ctrl+o` VSCodeVim jumplist shortcut does not return you to the correct place after using "Go to Definition" (`ctrl+]`): https://github.com/VSCodeVim/Vim/issues/3277 (The VSCode `Go Back` command (`ctrl+-`) does work, however.)
- This extension sets the `wordPattern` for 'markdown' in order to (1) enable proper completion of relative paths and (2) make it such that if you `cmd+shift+f` on a `#tag` the search will prefill with "#tag" and not just "tag":
<br />`vscode.languages.setLanguageConfiguration('markdown', { wordPattern: /([\#\.\/\\\w_]+)/ });`
Expand All @@ -90,7 +88,6 @@ Run `npm install` first.
- Provide better support for ignore patterns, eg, don't complete `file.md` if it is within `ignored_dir/`
- Should we support filename without extension, eg, assume `[[file]]` is a reference to `file.md`?
- Should we support links to headings? eg, `file.md#heading-text`?
- Add syntax highlighting and search for `#tags`. See [also](https://stackoverflow.com/questions/60293955/is-cmdshiftf-in-vscode-supposed-to-respect-the-editor-wordseparators-setting)

### Development and Release

Expand All @@ -99,7 +96,7 @@ To create a new release,
```sh
npm install
# bump version number in package.json
npm run vpackage # package the release, creates ,vsix
npm run vpackage # package the release, creates vsix
npm run vpublish # publish to store, see https://code.visualstudio.com/api/working-with-extensions/publishing-extension
# Will prompt for Azure Devops Personal Access Token, get fresh one at:
# https://dev.azure.com/andrewkortina/
Expand Down
48 changes: 28 additions & 20 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 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",
"description": "Navigate notes with [[wiki-links]] and #tags (like Bear, Roam, etc). Use Peek Definition to preview linked notes. Quickly creatre new notes with a command.",
"version": "0.0.4",
"description": "Navigate notes with [[wiki-links]] and #tags (like Bear, Roam, etc). Use Peek Definition to preview linked notes. Quickly create new notes with a command.",
"version": "0.0.5",
"publisher": "kortina",
"repository": {
"url": "https://github.com/kortina/vscode-markdown-notes.git",
Expand Down Expand Up @@ -81,6 +81,6 @@
"@typescript-eslint/parser": "^2.28.0",
"eslint": "^6.8.0",
"typescript": "^3.5.1",
"vsce": "^1.73.0"
"vsce": "^1.75.0"
}
}
72 changes: 53 additions & 19 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class WorkspaceTagList {
this.STARTED_INIT = true;
let files = (await workspace.findFiles('**/*'))
.filter(
// TODO: paramaterize extensions. Add $ to end?
// TODO: parameterize extensions. Add $ to end?
(f) => f.scheme == 'file' && f.path.match(/\.(md|markdown)/i)
)
.map((f) => {
Expand All @@ -73,11 +73,19 @@ interface ContextWord {
type: ContextWordType;
word: string;
hasExtension: boolean | null;
range: vscode.Range | undefined;
}

const NULL_CONTEXT_WORD = { type: ContextWordType.Null, word: '', hasExtension: null };
const NULL_CONTEXT_WORD = {
type: ContextWordType.Null,
word: '',
hasExtension: null,
range: undefined,
};
const TAG_REGEX___NO_ANCHORS = /\#[\w\-\_]+/i; // used to match tags that appear within lines
const TAG_REGEX_WITH_ANCHORS = /^\#[\w\-\_]+$/i; // used to match entire words
const WIKI_LINK_REGEX = /\[\[[\w\.\-\_\/\\]+/i; // [[wiki-link-regex
const MARKDOWN_WORD_PATTERN_OVERRIDE = /([\#\.\/\\\w_]+)/; // had to add [".", "/", "\"] to get relative path completion working and ["#"] to get tag completion working

function getContextWord(document: TextDocument, position: Position): ContextWord {
let contextWord: string;
Expand All @@ -88,35 +96,44 @@ function getContextWord(document: TextDocument, position: Position): ContextWord
regex = TAG_REGEX___NO_ANCHORS;
range = document.getWordRangeAtPosition(position, regex);
if (range) {
// here we do nothing to modify the range because the replacements
// will include the # character, so we want to keep the leading #
contextWord = document.getText(range);
if (contextWord) {
return {
type: ContextWordType.Tag,
word: contextWord.replace(/^\#+/, ''),
hasExtension: null,
range: range,
};
}
}

// [[wiki-link-regex
// regex = /[\w\.\-\_\/\\]+\.(md|markdown)/i;
regex = /\[\[[\w\.\-\_\/\\]+/i;
regex = WIKI_LINK_REGEX;
range = document.getWordRangeAtPosition(position, regex);
if (range) {
contextWord = document.getText(range);
// account for the (exactly) 2 [[ chars at beginning of the match
// since our replacement words do not contain [[ chars
let s = new vscode.Position(range.start.line, range.start.character + 2);
// keep the end
let r = new vscode.Range(s, range.end);
contextWord = document.getText(r);
if (contextWord) {
return {
type: ContextWordType.WikiLink,
word: contextWord.replace(/^\[+/, ''),
// TODO: paramaterize extensions. Add $ to end?
word: contextWord, // .replace(/^\[+/, ''),
// TODO: parameterize extensions. Add $ to end?
hasExtension: !!contextWord.match(/\.(md|markdown)/i),
range: r, // range,
};
}
}

return NULL_CONTEXT_WORD;
}

// perhaps there is a race condition in the setting of markdown wordPattern?
// ???????????????????????????????????? 🧐
class MarkdownFileCompletionItemProvider implements CompletionItemProvider {
public async provideCompletionItems(
document: TextDocument,
Expand All @@ -125,34 +142,46 @@ class MarkdownFileCompletionItemProvider implements CompletionItemProvider {
context: 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]}`);
///////////////////////////
// TODO: add handling for ContextWorkType.Tag
///////////////////////////
let items = [];
switch (contextWord.type) {
case ContextWordType.Null:
return [];
break;
case ContextWordType.Tag:
// console.debug(`ContextWordType.Tag`);
// console.debug(`TAG_WORD_SET: ${Array.from(WorkspaceTagList.TAG_WORD_SET)}`);
// console.debug(
// `contextWord.word: ${contextWord.word} TAG_WORD_SET: ${Array.from(
// WorkspaceTagList.TAG_WORD_SET
// )}`
// );
items = Array.from(WorkspaceTagList.TAG_WORD_SET).map((t) => {
let kind = CompletionItemKind.File;
let label = `${t}`; // cast to a string
return new CompletionItem(label, kind);
let item = new CompletionItem(label, kind);
if (contextWord && contextWord.range) {
item.range = contextWord.range;
}
return item;
});
return items;
break;
case ContextWordType.WikiLink:
let files = (await workspace.findFiles('**/*')).filter(
// TODO: paramaterize extensions. Add $ to end?
// TODO: parameterize extensions. Add $ to end?
(f) => f.scheme == 'file' && f.path.match(/\.(md|markdown)/i)
);
items = files.map((f) => {
let kind = CompletionItemKind.File;
let label = filenameForConvention(f, document);
return new CompletionItem(label, kind);
let item = new CompletionItem(label, kind);
if (contextWord && contextWord.range) {
item.range = contextWord.range;
}
return item;
});
return items;
break;
Expand Down Expand Up @@ -197,7 +226,7 @@ class MarkdownDefinitionProvider implements vscode.DefinitionProvider {
// However, only check for basenames in the entire project if:
if (useUniqueFilenames()) {
const filename = selectedWord;
// there should be exactly 1 file with name = selecteWord
// there should be exactly 1 file with name = selectedWord
files = (await workspace.findFiles('**/*')).filter((f) => {
return basename(f.path) == filename;
});
Expand Down Expand Up @@ -279,13 +308,18 @@ function newNote(context: vscode.ExtensionContext) {
);
}

const overrideMarkdownWordPattern = () => {
// console.debug('overrideMarkdownWordPattern');
vscode.languages.setLanguageConfiguration('markdown', {
wordPattern: MARKDOWN_WORD_PATTERN_OVERRIDE,
});
};

export function activate(context: vscode.ExtensionContext) {
// console.debug('vscode-markdown-notes.activate');
const md = { scheme: 'file', language: 'markdown' };
vscode.languages.setLanguageConfiguration('markdown', { wordPattern: /([\#\.\/\\\w_]+)/ });
overrideMarkdownWordPattern(); // still nec to get ../ to trigger suggestions in `relativePaths` mode

// const triggerCharacters = ['.', '#'];
// const triggerCharacters = [];
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider(md, new MarkdownFileCompletionItemProvider())
);
Expand Down

0 comments on commit 091df42

Please sign in to comment.