Skip to content

Commit 2fbf269

Browse files
authored
Support emoji completions in review comments (#8009)
* Support emoji completions in review comments * PR feedback * Improved logic
1 parent a4b870c commit 2fbf269

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

src/common/emoji.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ const emojiRegex = /:([-+_a-z0-9]+):/g;
1313
let emojiMap: Record<string, string> | undefined;
1414
let emojiMapPromise: Promise<void> | undefined;
1515

16-
export async function ensureEmojis(context: ExtensionContext) {
16+
export async function ensureEmojis(context: ExtensionContext): Promise<Record<string, string>> {
1717
if (emojiMap === undefined) {
1818
if (emojiMapPromise === undefined) {
1919
emojiMapPromise = loadEmojiMap(context);
2020
}
2121
await emojiMapPromise;
2222
}
23+
return emojiMap!;
2324
}
2425

2526
async function loadEmojiMap(context: ExtensionContext) {

src/extension.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { UriHandler } from './uriHandler';
4343
import { CommentDecorationProvider } from './view/commentDecorationProvider';
4444
import { CompareChanges } from './view/compareChangesTreeDataProvider';
4545
import { CreatePullRequestHelper } from './view/createPullRequestHelper';
46+
import { EmojiCompletionProvider } from './view/emojiCompletionProvider';
4647
import { FileTypeDecorationProvider } from './view/fileTypeDecorationProvider';
4748
import { GitHubCommitFileSystemProvider } from './view/githubFileContentProvider';
4849
import { getInMemPRFileSystemProvider } from './view/inMemPRContentProvider';
@@ -181,6 +182,12 @@ async function init(
181182
const reviewsManager = new ReviewsManager(context, reposManager, reviewManagers, prsTreeModel, tree, changesTree, telemetry, credentialStore, git, copilotRemoteAgentManager, notificationsManager);
182183
context.subscriptions.push(reviewsManager);
183184

185+
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(
186+
{ scheme: Schemes.Comment },
187+
new EmojiCompletionProvider(context),
188+
':'
189+
));
190+
184191
git.onDidChangeState(() => {
185192
Logger.appendLine(`Git initialization state changed: state=${git.state}`, ACTIVATION);
186193
reviewsManager.reviewManagers.forEach(reviewManager => reviewManager.updateState(true));
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import { ensureEmojis } from '../common/emoji';
8+
import { Schemes } from '../common/uri';
9+
10+
export class EmojiCompletionProvider implements vscode.CompletionItemProvider {
11+
private _emojiCompletions: vscode.CompletionItem[] = [];
12+
13+
constructor(private _context: vscode.ExtensionContext) {
14+
void this.buildEmojiCompletions();
15+
}
16+
17+
private async buildEmojiCompletions(): Promise<void> {
18+
const emojis = await ensureEmojis(this._context);
19+
20+
for (const [name, emoji] of Object.entries(emojis)) {
21+
const completionItem = new vscode.CompletionItem({ label: emoji, description: `:${name}:` }, vscode.CompletionItemKind.Text);
22+
completionItem.filterText = `:${name}:`;
23+
completionItem.sortText = name;
24+
this._emojiCompletions.push(completionItem);
25+
}
26+
}
27+
28+
provideCompletionItems(
29+
document: vscode.TextDocument,
30+
position: vscode.Position,
31+
_token: vscode.CancellationToken,
32+
context: vscode.CompletionContext
33+
): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
34+
// Only provide completions for comment documents
35+
if (document.uri.scheme !== Schemes.Comment) {
36+
return [];
37+
}
38+
39+
const word = document.getWordRangeAtPosition(position, /:([-+_a-z0-9]+:?)?/i);
40+
if (!word) {
41+
return [];
42+
}
43+
44+
// If invoked by trigger charcter, ignore if this is the start of an emoji (single ':') and there is no preceding space
45+
if (context.triggerKind === vscode.CompletionTriggerKind.TriggerCharacter) {
46+
if (word.end.character - word.start.character === 1 && word.start.character > 0) {
47+
const charBefore = document.getText(new vscode.Range(word.start.translate(0, -1), word.start));
48+
if (!/\s/.test(charBefore)) {
49+
return [];
50+
}
51+
}
52+
}
53+
54+
// Update the range on cached items directly
55+
for (const item of this._emojiCompletions) {
56+
item.range = word;
57+
}
58+
59+
return new vscode.CompletionList(this._emojiCompletions, false);
60+
}
61+
}

0 commit comments

Comments
 (0)