Files
wizgit-vscode-extension/src/features/search.ts
Terence Carrera 239ddbd362 feat: Add export functionality for issues and pull requests
- Implemented exportIssues and exportPullRequests functions to export data in markdown, CSV, and JSON formats.
- Created helper functions for generating markdown and CSV formats for issues and pull requests.
- Added showExportDialog for user interaction to select export options.
- Implemented saveToFile function to handle file saving.

feat: Implement favorites and recent repositories management

- Added functionality to add, remove, and check favorites for repositories.
- Implemented recent repositories tracking with a limit on the number of recent entries.
- Created a quick pick interface to show favorites and recent repositories.

feat: Introduce notifications management

- Implemented functions to fetch, mark as read, and manage notifications.
- Created a NotificationProvider class to display notifications in a tree view.
- Added functionality to show notifications in a modal with quick actions.

feat: Implement search functionality for repositories, issues, and pull requests

- Added searchRepositories, searchIssues, and searchPullRequests functions with filtering options.
- Implemented getLabels and getCollaborators functions for additional filtering capabilities.
- Created a showFilterQuickPick function for user interaction to select filters.

feat: Enhance status bar with shortcuts and dynamic updates

- Added default keyboard shortcuts for various commands.
- Implemented createStatusBarItem and updateStatusBar functions to manage status bar display.
- Created a showStatusMenu function for quick actions related to the extension.
2025-12-17 16:06:52 +08:00

266 lines
7.3 KiB
TypeScript

import * as vscode from 'vscode';
import fetch from 'node-fetch';
export interface SearchOptions {
query: string;
state?: 'open' | 'closed';
labels?: string[];
assignee?: string;
sort?: 'created' | 'updated' | 'popularity';
order?: 'asc' | 'desc';
}
export interface FilterOptions {
state?: 'open' | 'closed';
labels?: string[];
assignee?: string;
}
/**
* Search repositories across WizGIT
*/
export async function searchRepositories(
apiEndpoint: string,
token: string,
query: string,
page: number = 1,
limit: number = 30
): Promise<any> {
try {
const url = new URL(`${apiEndpoint}/repos/search`);
url.searchParams.append('q', query);
url.searchParams.append('page', page.toString());
url.searchParams.append('limit', limit.toString());
const response = await fetch(url.toString(), {
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Search failed: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Repository search error:', error);
throw error;
}
}
/**
* Search issues in a repository with filters
*/
export async function searchIssues(
apiEndpoint: string,
token: string,
owner: string,
repo: string,
options: SearchOptions & { page?: number; limit?: number }
): Promise<any> {
try {
const url = new URL(`${apiEndpoint}/repos/${owner}/${repo}/issues`);
url.searchParams.append('page', (options.page || 1).toString());
url.searchParams.append('limit', (options.limit || 30).toString());
if (options.query) {
url.searchParams.append('q', options.query);
}
if (options.state) {
url.searchParams.append('state', options.state);
}
if (options.sort) {
url.searchParams.append('sort', options.sort);
}
if (options.order) {
url.searchParams.append('order', options.order);
}
if (options.labels && options.labels.length > 0) {
url.searchParams.append('labels', options.labels.join(','));
}
if (options.assignee) {
url.searchParams.append('assignee', options.assignee);
}
const response = await fetch(url.toString(), {
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Search failed: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Issue search error:', error);
throw error;
}
}
/**
* Search pull requests with filters
*/
export async function searchPullRequests(
apiEndpoint: string,
token: string,
owner: string,
repo: string,
options: SearchOptions & { page?: number; limit?: number }
): Promise<any> {
try {
const url = new URL(`${apiEndpoint}/repos/${owner}/${repo}/pulls`);
url.searchParams.append('page', (options.page || 1).toString());
url.searchParams.append('limit', (options.limit || 30).toString());
if (options.query) {
url.searchParams.append('q', options.query);
}
if (options.state) {
url.searchParams.append('state', options.state);
}
if (options.sort) {
url.searchParams.append('sort', options.sort);
}
if (options.order) {
url.searchParams.append('order', options.order);
}
if (options.assignee) {
url.searchParams.append('assignee', options.assignee);
}
const response = await fetch(url.toString(), {
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Search failed: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('PR search error:', error);
throw error;
}
}
/**
* Get available labels for filtering
*/
export async function getLabels(
apiEndpoint: string,
token: string,
owner: string,
repo: string
): Promise<any[]> {
try {
const response = await fetch(`${apiEndpoint}/repos/${owner}/${repo}/labels`, {
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Failed to fetch labels: ${response.statusText}`);
}
return (await response.json()) as any[];
} catch (error) {
console.error('Get labels error:', error);
return [];
}
}
/**
* Get collaborators for assignee filtering
*/
export async function getCollaborators(
apiEndpoint: string,
token: string,
owner: string,
repo: string
): Promise<any[]> {
try {
const response = await fetch(`${apiEndpoint}/repos/${owner}/${repo}/collaborators`, {
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Failed to fetch collaborators: ${response.statusText}`);
}
return (await response.json()) as any[];
} catch (error) {
console.error('Get collaborators error:', error);
return [];
}
}
/**
* Show a quick filter picker for issues/PRs
*/
export async function showFilterQuickPick(
apiEndpoint: string,
token: string,
owner: string,
repo: string,
type: 'issues' | 'pulls'
): Promise<FilterOptions | null> {
const filterOptions: FilterOptions = {};
// State filter
const stateSelection = await vscode.window.showQuickPick(
['All', 'Open', 'Closed'],
{ placeHolder: 'Filter by state (optional)' }
);
if (stateSelection && stateSelection !== 'All') {
filterOptions.state = stateSelection === 'Open' ? 'open' : 'closed';
}
// Assignee filter
const collaborators = await getCollaborators(apiEndpoint, token, owner, repo);
if (collaborators.length > 0) {
const assigneeItems = ['No filter', ...collaborators.map(c => c.login)];
const assigneeSelection = await vscode.window.showQuickPick(
assigneeItems,
{ placeHolder: 'Filter by assignee (optional)' }
);
if (assigneeSelection && assigneeSelection !== 'No filter') {
filterOptions.assignee = assigneeSelection;
}
}
// Labels filter (only for issues)
if (type === 'issues') {
const labels = await getLabels(apiEndpoint, token, owner, repo);
if (labels.length > 0) {
const labelItems = labels.map(l => l.name);
const labelSelection = await vscode.window.showQuickPick(
labelItems,
{ placeHolder: 'Filter by label (optional)', canPickMany: true }
);
if (labelSelection && labelSelection.length > 0) {
filterOptions.labels = labelSelection;
}
}
}
return Object.keys(filterOptions).length > 0 ? filterOptions : null;
}