blob: 85abc799d027052fcb90babce5e3182d51d2f56f [file]
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {ContextItem, ContextItemType} from '../../api/ai-code-review';
/** Parses all potential context items from a give text string. */
export function searchForContextLinks(
text: string,
contextItemTypes: readonly ContextItemType[]
): ContextItem[] {
const contextItems: ContextItem[] = [];
for (const contextItemType of contextItemTypes) {
const matches = text.matchAll(new RegExp(contextItemType.regex, 'g'));
for (const match of matches) {
const url = match[0];
const contextItem = contextItemType.parse(url);
if (
contextItem &&
!contextItems.some(item => contextItemEquals(item, contextItem))
) {
contextItems.push(contextItem);
}
}
}
return contextItems;
}
/**
* Parses a link as a context item. Returns undefined if the link is not a
* supported context item.
*/
export function parseLink(
url: string,
contextItemTypes: readonly ContextItemType[]
): ContextItem | undefined {
url = url.replace(/\s+/g, ''); // Remove all whitespaces.
for (const contextItemType of contextItemTypes) {
const contextItem = contextItemType.parse(url);
if (contextItem) return contextItem;
}
return undefined;
}
/** Implementation of the equals method for ContextItem. */
export function contextItemEquals(a: ContextItem, b: ContextItem): boolean {
return a.type_id === b.type_id && a.identifier === b.identifier;
}
/**
* Searches for bug numbers in a commit message that are not linked.
* It searches for keywords like "bug:", "fixes=", etc. followed by a
* long number. It ignores existing b/ links and URLs.
*/
export function searchForBugsInCommitMessage(
commitMessage: string,
contextItemTypes: readonly ContextItemType[]
): ContextItem[] {
const buganizerType = contextItemTypes.find(t => t.id === 'buganizer');
if (!buganizerType) return [];
const lines = commitMessage.toLowerCase().split('\n');
// This regex is to identify lines that might contain bug numbers.
// TODO(b/484367705) support commentlinks defined by each host.
const keywordRegex =
// prettier-ignore
/\b(?:bug|fix|issue|fixed|fixes|bugfix|google-bug-id|cts-coverage-bug|crs-fixed)\b/i;
const bugIds = new Set<string>();
for (const line of lines) {
if (!keywordRegex.test(line)) continue;
// Remove URLs and b/ links before searching for numbers, because they are
// handled by searchForContextLinks.
const cleanedLine = line
.replace(/https?:\/\/[^\s]+/g, '')
.replace(/b\/\d+/g, '');
// This regex finds nums with at least 8 digits, which are likely bug ids.
const numberRegex = /\b\d{8,}\b/g;
const numbers = cleanedLine.match(numberRegex) ?? [];
for (const num of numbers) {
bugIds.add(num);
}
}
// Convert bugIds to ContextItems.
const contextItems: ContextItem[] = [];
for (const num of bugIds) {
const contextItem = buganizerType.parse(`b/${num}`);
if (contextItem) contextItems.push(contextItem);
}
return contextItems;
}