in src/github/folderRepositoryManager.ts [241:421]
provideCompletionItems: async (document, position, _token) => {
try {
const query = JSON.parse(document.uri.query);
if (compareIgnoreCase(query.extensionId, EXTENSION_ID) !== 0) {
return;
}
const wordRange = document.getWordRangeAtPosition(
position,
/@([a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38})?/i,
);
if (!wordRange || wordRange.isEmpty) {
return;
}
let prRelatedusers: { login: string; name?: string }[] = [];
const fileRelatedUsersNames: { [key: string]: boolean } = {};
let mentionableUsers: { [key: string]: { login: string; name?: string }[] } = {};
let prNumber: number | undefined;
let remoteName: string | undefined;
const activeTextEditors = vscode.window.visibleTextEditors;
if (activeTextEditors.length) {
const visiblePREditor = activeTextEditors.find(
editor => editor.document.uri.scheme === 'pr',
);
if (visiblePREditor) {
const params = fromPRUri(visiblePREditor.document.uri);
prNumber = params!.prNumber;
remoteName = params!.remoteName;
} else if (this._activePullRequest) {
prNumber = this._activePullRequest.number;
remoteName = this._activePullRequest.remote.remoteName;
}
if (lastPullRequest && prNumber && prNumber === lastPullRequest.number) {
return cachedUsers;
}
}
const prRelatedUsersPromise = new Promise<void>(async resolve => {
if (prNumber && remoteName) {
Logger.debug('get Timeline Events and parse users', FolderRepositoryManager.ID);
if (lastPullRequest && lastPullRequest.number === prNumber) {
return lastPullRequestTimelineEvents;
}
const githubRepo = this._githubRepositories.find(
repo => repo.remote.remoteName === remoteName,
);
if (githubRepo) {
lastPullRequest = await githubRepo.getPullRequest(prNumber);
lastPullRequestTimelineEvents = await lastPullRequest!.getTimelineEvents();
}
prRelatedusers = getRelatedUsersFromTimelineEvents(lastPullRequestTimelineEvents);
resolve();
}
resolve();
});
const fileRelatedUsersNamesPromise = new Promise<void>(async resolve => {
if (activeTextEditors.length) {
try {
Logger.debug('git blame and parse users', FolderRepositoryManager.ID);
const fsPath = path.resolve(activeTextEditors[0].document.uri.fsPath);
let blames: string | undefined;
if (this._gitBlameCache[fsPath]) {
blames = this._gitBlameCache[fsPath];
} else {
blames = await this.repository.blame(fsPath);
this._gitBlameCache[fsPath] = blames;
}
const blameLines = blames.split('\n');
for (const line of blameLines) {
const matches = /^\w{11} \S*\s*\((.*)\s*\d{4}\-/.exec(line);
if (matches && matches.length === 2) {
const name = matches[1].trim();
fileRelatedUsersNames[name] = true;
}
}
} catch (err) {
Logger.debug(err, FolderRepositoryManager.ID);
}
}
resolve();
});
const getMentionableUsersPromise = new Promise<void>(async resolve => {
Logger.debug('get mentionable users', FolderRepositoryManager.ID);
mentionableUsers = await this.getMentionableUsers();
resolve();
});
await Promise.all([
prRelatedUsersPromise,
fileRelatedUsersNamesPromise,
getMentionableUsersPromise,
]);
cachedUsers = [];
const prRelatedUsersMap: { [key: string]: { login: string; name?: string } } = {};
Logger.debug('prepare user suggestions', FolderRepositoryManager.ID);
prRelatedusers.forEach(user => {
if (!prRelatedUsersMap[user.login]) {
prRelatedUsersMap[user.login] = user;
}
});
const secondMap: { [key: string]: boolean } = {};
for (const mentionableUserGroup in mentionableUsers) {
for (const user of mentionableUsers[mentionableUserGroup]) {
if (!prRelatedUsersMap[user.login] && !secondMap[user.login]) {
secondMap[user.login] = true;
let priority = 2;
if (
fileRelatedUsersNames[user.login] ||
(user.name && fileRelatedUsersNames[user.name])
) {
priority = 1;
}
if (prRelatedUsersMap[user.login]) {
priority = 0;
}
cachedUsers.push({
label: user.login,
insertText: user.login,
filterText:
`${user.login}` +
(user.name && user.name !== user.login
? `_${user.name.toLowerCase().replace(' ', '_')}`
: ''),
sortText: `${priority}_${user.login}`,
detail: user.name,
kind: vscode.CompletionItemKind.User,
login: user.login,
uri: this.repository.rootUri,
});
}
}
}
for (const user in prRelatedUsersMap) {
if (!secondMap[user]) {
// if the mentionable api call fails partially, we should still populate related users from timeline events into the completion list
cachedUsers.push({
label: prRelatedUsersMap[user].login,
insertText: `${prRelatedUsersMap[user].login}`,
filterText:
`${prRelatedUsersMap[user].login}` +
(prRelatedUsersMap[user].name &&
prRelatedUsersMap[user].name !== prRelatedUsersMap[user].login
? `_${prRelatedUsersMap[user].name!.toLowerCase().replace(' ', '_')}`
: ''),
sortText: `0_${prRelatedUsersMap[user].login}`,
detail: prRelatedUsersMap[user].name,
kind: vscode.CompletionItemKind.User,
login: prRelatedUsersMap[user].login,
uri: this.repository.rootUri,
});
}
}
Logger.debug('done', FolderRepositoryManager.ID);
return cachedUsers;
} catch (e) {
return [];
}
},