Skip to content

Commit

Permalink
Also add projects to the new issue file
Browse files Browse the repository at this point in the history
Fixes #3062
  • Loading branch information
alexr00 committed Feb 13, 2024
1 parent 4865c64 commit 4fd5d85
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 11 deletions.
3 changes: 1 addition & 2 deletions src/github/createPRViewProviderNew.ts
Original file line number Diff line number Diff line change
Expand Up @@ -759,9 +759,8 @@ export class CreatePullRequestViewProviderNew extends WebviewViewBase implements
if (!githubRepo) {
return;
}
const isInOrganization = !!(await githubRepo.getMetadata()).organization;
await new Promise<void>((resolve) => {
getProjectFromQuickPick(this._folderRepositoryManager, githubRepo, githubRepo.remote.remoteName, isInOrganization, this.projects, async (projects) => {
getProjectFromQuickPick(this._folderRepositoryManager, githubRepo, this.projects, async (projects) => {
this.projects = projects;
this._postMessage({
command: 'set-projects',
Expand Down
6 changes: 6 additions & 0 deletions src/github/folderRepositoryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,12 @@ export class FolderRepositoryManager implements vscode.Disposable {
return globalStateProjects ?? this.createFetchOrgProjectsPromise();
}

async getAllProjects(githubRepository: GitHubRepository, clearOrgCache?: boolean): Promise<IProject[]> {
const isInOrganization = !!(await githubRepository.getMetadata()).organization;
const [repoProjects, orgProjects] = (await Promise.all([githubRepository.getProjects(), (isInOrganization ? this.getOrgProjects(clearOrgCache) : undefined)]));
return [...(repoProjects ?? []), ...(orgProjects ? orgProjects[githubRepository.remote.remoteName] : [])];
}

async getOrgTeamsCount(repository: GitHubRepository): Promise<number> {
if ((await repository.getMetadata()).organization) {
return repository.getOrgTeamsCount();
Expand Down
2 changes: 1 addition & 1 deletion src/github/pullRequestOverview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
}

private async changeProjects(message: IRequestMessage<void>): Promise<void> {
return getProjectFromQuickPick(this._folderRepositoryManager, this._item.githubRepository, this._item.remote.remoteName, this._item.base.isInOrganization, this._item.item.projectItems?.map(item => item.project), (project) => this.updateProjects(project, message));
return getProjectFromQuickPick(this._folderRepositoryManager, this._item.githubRepository, this._item.item.projectItems?.map(item => item.project), (project) => this.updateProjects(project, message));
}

private async updateProjects(projects: IProject[] | undefined, message: IRequestMessage<void>) {
Expand Down
5 changes: 2 additions & 3 deletions src/github/quickPicks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,11 @@ function isProjectQuickPickItem(x: vscode.QuickPickItem | ProjectQuickPickItem):
return !!(x as ProjectQuickPickItem).id && !!(x as ProjectQuickPickItem).project;
}

export async function getProjectFromQuickPick(folderRepoManager: FolderRepositoryManager, githubRepository: GitHubRepository, remoteName: string, isInOrganization: boolean, currentProjects: IProject[] | undefined, callback: (projects: IProject[]) => Promise<void>): Promise<void> {
export async function getProjectFromQuickPick(folderRepoManager: FolderRepositoryManager, githubRepository: GitHubRepository, currentProjects: IProject[] | undefined, callback: (projects: IProject[]) => Promise<void>): Promise<void> {
try {
let selectedItems: vscode.QuickPickItem[] = [];
async function getProjectOptions(): Promise<(ProjectQuickPickItem | vscode.QuickPickItem)[]> {
const [repoProjects, orgProjects] = (await Promise.all([githubRepository.getProjects(), (isInOrganization ? folderRepoManager.getOrgProjects() : undefined)]));
const projects = [...(repoProjects ?? []), ...(orgProjects ? orgProjects[remoteName] : [])];
const projects = await folderRepoManager.getAllProjects(githubRepository);
if (!projects || !projects.length) {
return [
{
Expand Down
27 changes: 25 additions & 2 deletions src/issues/issueFeatureRegistrar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { ITelemetry } from '../common/telemetry';
import { OctokitCommon } from '../github/common';
import { FolderRepositoryManager, PullRequestDefaults } from '../github/folderRepositoryManager';
import { IProject } from '../github/interface';
import { IssueModel } from '../github/issueModel';
import { RepositoriesManager } from '../github/repositoriesManager';
import { getRepositoryForFile, ISSUE_OR_URL_EXPRESSION, parseIssueExpressionOutput } from '../github/utils';
Expand All @@ -34,6 +35,7 @@ import {
NEW_ISSUE_SCHEME,
NewIssueCache,
NewIssueFileCompletionProvider,
PROJECTS,
} from './issueFile';
import { IssueHoverProvider } from './issueHoverProvider';
import { openCodeLink } from './issueLinkLookup';
Expand Down Expand Up @@ -647,6 +649,7 @@ export class IssueFeatureRegistrar implements vscode.Disposable {
metadata.assignees,
metadata.labels,
metadata.milestone,
metadata.projects,
this.createIssueInfo?.lineNumber,
this.createIssueInfo?.insertIndex,
metadata.originUri
Expand Down Expand Up @@ -926,7 +929,7 @@ export class IssueFeatureRegistrar implements vscode.Disposable {
title = quickInput.value;
if (title) {
quickInput.busy = true;
await this.doCreateIssue(document, newIssue, title, body, assignee, undefined, undefined, lineNumber, insertIndex);
await this.doCreateIssue(document, newIssue, title, body, assignee, undefined, undefined, undefined, lineNumber, insertIndex);
quickInput.busy = false;
}
quickInput.hide();
Expand Down Expand Up @@ -965,11 +968,13 @@ export class IssueFeatureRegistrar implements vscode.Disposable {
}`;
const labelLine = `${LABELS} `;
const milestoneLine = `${MILESTONE} `;
const projectsLine = `${PROJECTS} `;
const cached = this._newIssueCache.get();
const text = (cached && cached !== '') ? cached : `${title ?? vscode.l10n.t('Issue Title')}\n
${assigneeLine}
${labelLine}
${milestoneLine}\n
${milestoneLine}
${projectsLine}\n
${body ?? ''}\n
<!-- ${vscode.l10n.t('Edit the body of your new issue then click the ✓ \"Create Issue\" button in the top right of the editor. The first line will be the issue title. Assignees and Labels follow after a blank line. Leave an empty line before beginning the body of the issue.')} -->`;
await vscode.workspace.fs.writeFile(bodyPath, this.stringToUint8Array(text));
Expand All @@ -987,6 +992,13 @@ ${body ?? ''}\n
color: new vscode.ThemeColor('issues.newIssueDecoration'),
},
});
const projectsDecoration = vscode.window.createTextEditorDecorationType({
after: {
contentText: vscode.l10n.t(' Comma-separated projects.'),
fontStyle: 'italic',
color: new vscode.ThemeColor('issues.newIssueDecoration'),
},
});
const editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(textEditor => {
if (textEditor?.document.uri.scheme === NEW_ISSUE_SCHEME) {
const assigneeFullLine = textEditor.document.lineAt(2);
Expand All @@ -1004,6 +1016,12 @@ ${body ?? ''}\n
new vscode.Range(new vscode.Position(3, 0), new vscode.Position(3, labelFullLine.text.length)),
]);
}
const projectsFullLine = textEditor.document.lineAt(5);
if (projectsFullLine.text.startsWith(PROJECTS)) {
textEditor.setDecorations(projectsDecoration, [
new vscode.Range(new vscode.Position(5, 0), new vscode.Position(5, projectsFullLine.text.length)),
]);
}
}
});

Expand Down Expand Up @@ -1149,6 +1167,7 @@ ${body ?? ''}\n
assignees: string[] | undefined,
labels: string[] | undefined,
milestone: number | undefined,
projects: IProject[] | undefined,
lineNumber: number | undefined,
insertIndex: number | undefined,
originUri?: vscode.Uri,
Expand Down Expand Up @@ -1189,12 +1208,16 @@ ${body ?? ''}\n
labels,
milestone
};

if (!(await this.verifyLabels(folderManager, createParams))) {
return false;
}
progress.report({ message: vscode.l10n.t('Creating issue in {0}...', `${createParams.owner}/${createParams.repo}`) });
const issue = await folderManager.createIssue(createParams);
if (issue) {
if (projects) {
await issue.updateProjects(projects);
}
if (document !== undefined && insertIndex !== undefined && lineNumber !== undefined) {
const edit: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
const insertText: string =
Expand Down
35 changes: 32 additions & 3 deletions src/issues/issueFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@

import * as vscode from 'vscode';
import { FolderRepositoryManager, PullRequestDefaults } from '../github/folderRepositoryManager';
import { IProject } from '../github/interface';
import { RepositoriesManager } from '../github/repositoriesManager';

export const NEW_ISSUE_SCHEME = 'newIssue';
export const NEW_ISSUE_FILE = 'NewIssue.md';
export const ASSIGNEES = vscode.l10n.t('Assignees:');
export const LABELS = vscode.l10n.t('Labels:');
export const MILESTONE = vscode.l10n.t('Milestone:');
export const PROJECTS = vscode.l10n.t('Projects:');

const NEW_ISSUE_CACHE = 'newIssue.cache';

Expand Down Expand Up @@ -91,7 +93,7 @@ export class NewIssueFileCompletionProvider implements vscode.CompletionItemProv
_context: vscode.CompletionContext,
): Promise<vscode.CompletionItem[]> {
const line = document.lineAt(position.line).text;
if (!line.startsWith(LABELS) && !line.startsWith(MILESTONE)) {
if (!line.startsWith(LABELS) && !line.startsWith(MILESTONE) && !line.startsWith(PROJECTS)) {
return [];
}
const originFile = extractIssueOriginFromQuery(document.uri);
Expand All @@ -108,6 +110,8 @@ export class NewIssueFileCompletionProvider implements vscode.CompletionItemProv
return this.provideLabelCompletionItems(folderManager, defaults);
} else if (line.startsWith(MILESTONE)) {
return this.provideMilestoneCompletionItems(folderManager);
} else if (line.startsWith(PROJECTS)) {
return this.provideProjectCompletionItems(folderManager);
} else {
return [];
}
Expand All @@ -131,6 +135,15 @@ export class NewIssueFileCompletionProvider implements vscode.CompletionItemProv
return item;
});
}

private async provideProjectCompletionItems(folderManager: FolderRepositoryManager): Promise<vscode.CompletionItem[]> {
const projects = await (await folderManager.getPullRequestDefaultRepo())?.getProjects() ?? [];
return projects.map(project => {
const item = new vscode.CompletionItem(project.title, vscode.CompletionItemKind.Event);
item.commitCharacters = [' ', ','];
return item;
});
}
}

export class NewIssueCache {
Expand All @@ -154,7 +167,7 @@ export class NewIssueCache {
}
}

export async function extractMetadataFromFile(repositoriesManager: RepositoriesManager): Promise<{ labels: string[] | undefined, milestone: number | undefined, assignees: string[] | undefined, title: string, body: string | undefined, originUri: vscode.Uri } | undefined> {
export async function extractMetadataFromFile(repositoriesManager: RepositoriesManager): Promise<{ labels: string[] | undefined, milestone: number | undefined, projects: IProject[] | undefined, assignees: string[] | undefined, title: string, body: string | undefined, originUri: vscode.Uri } | undefined> {
let text: string;
if (
!vscode.window.activeTextEditor ||
Expand Down Expand Up @@ -232,6 +245,22 @@ export async function extractMetadataFromFile(repositoriesManager: RepositoriesM
text = text.substring(lines[0].length).trim();
}
}
let projects: IProject[] | undefined;
if (text.startsWith(PROJECTS)) {
const lines = text.split(/\r\n|\n/, 1);
if (lines.length === 1) {
const repoProjects = await folderManager.getAllProjects(repo);
projects = lines[0].substring(PROJECTS.length)
.split(',')
.map(value => {
value = value.trim();
return repoProjects.find(project => project.title === value);
})
.filter<IProject>((project): project is IProject => !!project);

text = text.substring(lines[0].length).trim();
}
}
const body = text ?? '';
return { labels, milestone, assignees, title, body, originUri };
return { labels, milestone, projects, assignees, title, body, originUri };
}

0 comments on commit 4fd5d85

Please sign in to comment.