Update regex for more robust detection of tags

Update the regex used for more robust detection of tags. Replace the
negative lookahead assertion `\s` with `[ \t]` (allow for `\n`), and
add `#` to the class so that `##` is ignored.

Attempted to add the negation `^[0-9p{L}p{Emoji}p{N}-/]` to the
negative look ahead. This was to exclude items like `#$`, `#&` that
can't be tags. However my regex-fu was insufficient.

Instead, if the regex match is to a single `#`, ensure it is the
character to the left of the cursor. Example

  `this is text #%|`

where the `|` represents the cursor. The `TAG_REGEX`
will match the `#` at index 13. However since the cursor is at 15, the
Completion provider will not run.

Update the tests to cover these situations and add them all to a sub-
`describe` block labeled by the bug issue number #1189
This commit is contained in:
Jim Graham
2023-04-15 09:56:07 -04:00
parent cade1a8d8d
commit d1ba3f8322
2 changed files with 94 additions and 31 deletions

View File

@@ -66,36 +66,6 @@ describe('Tag Completion', () => {
expect(tags).toBeNull();
});
it('should provide multiple suggestions when typing #, issue #1189', async () => {
const { uri } = await createFile(`# Title
#`);
const { doc } = await showInEditor(uri);
const provider = new TagCompletionProvider(foamTags);
const tags = await provider.provideCompletionItems(
doc,
new vscode.Position(2, 1)
);
expect(tags.items.length).toEqual(3);
});
it('should not provide a suggestion when typing `# `, issue #1189', async () => {
const { uri } = await createFile(`# Title
# `);
const { doc } = await showInEditor(uri);
const provider = new TagCompletionProvider(foamTags);
const tags = await provider.provideCompletionItems(
doc,
new vscode.Position(2, 2)
);
expect(foamTags.tags.get('primary')).toBeTruthy();
expect(tags).toBeNull();
});
it('should provide a suggestion when typing #prim', async () => {
const { uri } = await createFile('#prim');
const { doc } = await showInEditor(uri);
@@ -137,4 +107,84 @@ describe('Tag Completion', () => {
expect(foamTags.tags.get('primary')).toBeTruthy();
expect(tags).toBeNull();
});
describe('has robust triggering #1189', () => {
it('should provide multiple suggestions when typing #', async () => {
const { uri } = await createFile(`# Title
#`);
const { doc } = await showInEditor(uri);
const provider = new TagCompletionProvider(foamTags);
const tags = await provider.provideCompletionItems(
doc,
new vscode.Position(2, 1)
);
expect(tags.items.length).toEqual(3);
});
it('should provide multiple suggestions when typing # at EOL', async () => {
const { uri } = await createFile(`# Title
#
more text
`);
const { doc } = await showInEditor(uri);
const provider = new TagCompletionProvider(foamTags);
const tags = await provider.provideCompletionItems(
doc,
new vscode.Position(2, 1)
);
expect(tags.items.length).toEqual(3);
});
it('should not provide a suggestion when typing `# `', async () => {
const { uri } = await createFile(`# Title
# `);
const { doc } = await showInEditor(uri);
const provider = new TagCompletionProvider(foamTags);
const tags = await provider.provideCompletionItems(
doc,
new vscode.Position(2, 2)
);
expect(foamTags.tags.get('primary')).toBeTruthy();
expect(tags).toBeNull();
});
it('should not provide a suggestion when typing `#{non-match}`', async () => {
const { uri } = await createFile(`# Title
#$`);
const { doc } = await showInEditor(uri);
const provider = new TagCompletionProvider(foamTags);
const tags = await provider.provideCompletionItems(
doc,
new vscode.Position(2, 2)
);
expect(foamTags.tags.get('primary')).toBeTruthy();
expect(tags).toBeNull();
});
it('should not provide a suggestion when typing `##`', async () => {
const { uri } = await createFile(`# Title
##`);
const { doc } = await showInEditor(uri);
const provider = new TagCompletionProvider(foamTags);
const tags = await provider.provideCompletionItems(
doc,
new vscode.Position(2, 2)
);
expect(foamTags.tags.get('primary')).toBeTruthy();
expect(tags).toBeNull();
});
});
});

View File

@@ -7,7 +7,7 @@ import { mdDocSelector } from '../utils';
// this regex is different from HASHTAG_REGEX in that it does not look for a
// #+character. It uses a negative look-ahead for `# `
const TAG_REGEX =
/(?<=^|\s)#(?!(\s+))([0-9]*[\p{L}\p{Emoji_Presentation}\p{N}/_-]*)/gmu;
/(?<=^|\s)#(?![ \t#])([0-9]*[\p{L}\p{Emoji_Presentation}\p{N}/_-]*)/gu;
const feature: FoamFeature = {
activate: async (
@@ -44,6 +44,19 @@ export class TagCompletionProvider
return null;
}
// check the match group length.
// if the match is only '#', the character to the left of cursor should
// also be `#`. If it isn't, we didn't match the
// `[0-9]*[\p{L}\p{Emoji_Presentation}\p{N}/_-]` group
// This excludes things like `#&`
const matchText = requiresAutocomplete[requiresAutocomplete.length - 1];
if (
matchText === '#' &&
cursorPrefix.charAt(position.character - 1) !== '#'
) {
return null;
}
const completionTags = [];
[...this.foamTags.tags].forEach(([tag]) => {
const item = new vscode.CompletionItem(