Skip to content

Commit d981b20

Browse files
axosoft-ramintsergeibbb
authored andcommitted
Barebones 'start work' command for hacking/experimenting
(#3621, #3698)
1 parent 19a3956 commit d981b20

File tree

5 files changed

+196
-2
lines changed

5 files changed

+196
-2
lines changed

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5892,6 +5892,12 @@
58925892
"category": "GitLens",
58935893
"icon": "$(rocket)"
58945894
},
5895+
{
5896+
"command": "gitlens.startWork",
5897+
"title": "Start Work",
5898+
"category": "GitLens",
5899+
"icon": "$(rocket)"
5900+
},
58955901
{
58965902
"command": "gitlens.showLaunchpadView",
58975903
"title": "Show Launchpad View",
@@ -10350,6 +10356,10 @@
1035010356
"command": "gitlens.showLaunchpad",
1035110357
"when": "gitlens:enabled"
1035210358
},
10359+
{
10360+
"command": "gitlens.startWork",
10361+
"when": "gitlens:enabled"
10362+
},
1035310363
{
1035410364
"command": "gitlens.showLaunchpadView",
1035510365
"when": "gitlens:enabled"

src/commands/quickWizard.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { Commands } from '../constants.commands';
22
import type { Container } from '../container';
33
import type { LaunchpadCommandArgs } from '../plus/launchpad/launchpad';
4+
import type { StartWorkCommandArgs } from '../plus/startWork/startWork';
45
import { command } from '../system/vscode/command';
56
import type { CommandContext } from './base';
67
import type { QuickWizardCommandArgsWithCompletion } from './quickWizard.base';
78
import { QuickWizardCommandBase } from './quickWizard.base';
89

9-
export type QuickWizardCommandArgs = LaunchpadCommandArgs;
10+
export type QuickWizardCommandArgs = LaunchpadCommandArgs | StartWorkCommandArgs;
1011

1112
@command()
1213
export class QuickWizardCommand extends QuickWizardCommandBase {
1314
constructor(container: Container) {
14-
super(container, [Commands.ShowLaunchpad]);
15+
super(container, [Commands.ShowLaunchpad, Commands.StartWork]);
1516
}
1617

1718
protected override preExecute(
@@ -22,6 +23,9 @@ export class QuickWizardCommand extends QuickWizardCommandBase {
2223
case Commands.ShowLaunchpad:
2324
return this.execute({ command: 'launchpad', ...args });
2425

26+
case Commands.StartWork:
27+
return this.execute({ command: 'startWork', ...args });
28+
2529
default:
2630
return this.execute(args);
2731
}

src/commands/quickWizard.utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { StoredRecentUsage } from '../constants.storage';
22
import type { Container } from '../container';
33
import { LaunchpadCommand } from '../plus/launchpad/launchpad';
4+
import { StartWorkCommand } from '../plus/startWork/startWork';
45
import { configuration } from '../system/vscode/configuration';
56
import { getContext } from '../system/vscode/context';
67
import { BranchGitCommand } from './git/branch';
@@ -112,6 +113,10 @@ export class QuickWizardRootStep implements QuickPickStep<QuickCommand> {
112113
if (args?.command === 'launchpad') {
113114
this.hiddenItems.push(new LaunchpadCommand(container, args));
114115
}
116+
117+
if (args?.command === 'startWork') {
118+
this.hiddenItems.push(new StartWorkCommand(container));
119+
}
115120
}
116121

117122
private _command: QuickCommand | undefined;

src/constants.commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ export const enum Commands {
223223
ShowTimelineView = 'gitlens.showTimelineView',
224224
ShowWorktreesView = 'gitlens.showWorktreesView',
225225
ShowWorkspacesView = 'gitlens.showWorkspacesView',
226+
StartWork = 'gitlens.startWork',
226227
StashApply = 'gitlens.stashApply',
227228
StashSave = 'gitlens.stashSave',
228229
StashSaveFiles = 'gitlens.stashSaveFiles',

src/plus/startWork/startWork.ts

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { Uri } from 'vscode';
2+
import type {
3+
PartialStepState,
4+
StepGenerator,
5+
StepResultGenerator,
6+
StepSelection,
7+
StepState,
8+
} from '../../commands/quickCommand';
9+
import {
10+
canPickStepContinue,
11+
createPickStep,
12+
endSteps,
13+
QuickCommand,
14+
StepResultBreak,
15+
} from '../../commands/quickCommand';
16+
import { proBadge } from '../../constants';
17+
import { HostingIntegrationId } from '../../constants.integrations';
18+
import type { Container } from '../../container';
19+
import type { SearchedIssue } from '../../git/models/issue';
20+
import type { QuickPickItemOfT } from '../../quickpicks/items/common';
21+
import { createDirectiveQuickPickItem, Directive } from '../../quickpicks/items/directive';
22+
import { fromNow } from '../../system/date';
23+
24+
export type StartWorkItem = {
25+
item: SearchedIssue;
26+
};
27+
28+
export type StartWorkResult = { items: StartWorkItem[] };
29+
30+
interface Context {
31+
result: StartWorkResult;
32+
title: string;
33+
}
34+
35+
interface State {
36+
item?: StartWorkItem;
37+
action?: StartWorkAction;
38+
}
39+
40+
type StartWorkStepState<T extends State = State> = RequireSome<StepState<T>, 'item'>;
41+
42+
export type StartWorkAction = 'start';
43+
44+
export interface StartWorkCommandArgs {
45+
readonly command: 'startWork';
46+
}
47+
48+
function assertsStartWorkStepState(state: StepState<State>): asserts state is StartWorkStepState {
49+
if (state.item != null) return;
50+
51+
debugger;
52+
throw new Error('Missing item');
53+
}
54+
55+
export class StartWorkCommand extends QuickCommand<State> {
56+
constructor(container: Container) {
57+
super(container, 'startWork', 'startWork', `Start Work\u00a0\u00a0${proBadge}`, {
58+
description: 'Start work on an issue',
59+
});
60+
61+
this.initialState = {
62+
counter: 0,
63+
};
64+
}
65+
66+
protected async *steps(state: PartialStepState<State>): StepGenerator {
67+
if (this.container.git.isDiscoveringRepositories) {
68+
await this.container.git.isDiscoveringRepositories;
69+
}
70+
71+
const context: Context = {
72+
result: { items: [] },
73+
title: this.title,
74+
};
75+
76+
while (this.canStepsContinue(state)) {
77+
context.title = this.title;
78+
79+
await updateContextItems(this.container, context);
80+
81+
if (state.counter < 1 || state.item == null) {
82+
const result = yield* this.pickIssueStep(state, context);
83+
if (result === StepResultBreak) continue;
84+
state.item = result;
85+
}
86+
87+
assertsStartWorkStepState(state);
88+
state.action = 'start';
89+
90+
if (typeof state.action === 'string') {
91+
switch (state.action) {
92+
case 'start':
93+
startWork(state.item.item);
94+
break;
95+
}
96+
}
97+
98+
endSteps(state);
99+
}
100+
101+
return state.counter < 0 ? StepResultBreak : undefined;
102+
}
103+
104+
private *pickIssueStep(state: StepState<State>, context: Context): StepResultGenerator<StartWorkItem> {
105+
const buildIssueItem = (i: StartWorkItem) => {
106+
return {
107+
label:
108+
i.item.issue.title.length > 60 ? `${i.item.issue.title.substring(0, 60)}...` : i.item.issue.title,
109+
// description: `${i.repoAndOwner}#${i.id}, by @${i.author}`,
110+
description: `\u00a0 ${i.item.issue.repository?.owner ?? ''}/${i.item.issue.repository?.repo ?? ''}#${
111+
i.item.issue.id
112+
} \u00a0`,
113+
detail: ` ${fromNow(i.item.issue.updatedDate)} by @${i.item.issue.author.name}`,
114+
iconPath: i.item.issue.author?.avatarUrl != null ? Uri.parse(i.item.issue.author.avatarUrl) : undefined,
115+
item: i,
116+
picked: i.item.issue.id === state.item?.item?.issue.id,
117+
};
118+
};
119+
120+
const getItems = (result: StartWorkResult) => {
121+
const items: QuickPickItemOfT<StartWorkItem>[] = [];
122+
123+
if (result.items?.length) {
124+
items.push(...result.items.map(buildIssueItem));
125+
}
126+
127+
return items;
128+
};
129+
130+
function getItemsAndPlaceholder() {
131+
if (!context.result.items.length) {
132+
return {
133+
placeholder: 'All done! Take a vacation',
134+
items: [createDirectiveQuickPickItem(Directive.Cancel, undefined, { label: 'OK' })],
135+
};
136+
}
137+
138+
return {
139+
placeholder: 'Choose an item to focus on',
140+
items: getItems(context.result),
141+
};
142+
}
143+
144+
const { items, placeholder } = getItemsAndPlaceholder();
145+
146+
const step = createPickStep({
147+
title: context.title,
148+
placeholder: placeholder,
149+
matchOnDescription: true,
150+
matchOnDetail: true,
151+
items: items,
152+
});
153+
154+
const selection: StepSelection<typeof step> = yield step;
155+
if (!canPickStepContinue(step, state, selection)) {
156+
return StepResultBreak;
157+
}
158+
const element = selection[0];
159+
return { ...element.item };
160+
}
161+
}
162+
163+
async function updateContextItems(container: Container, context: Context) {
164+
context.result = {
165+
items:
166+
(await container.integrations.getMyIssues([HostingIntegrationId.GitHub]))?.map(i => ({
167+
item: i,
168+
})) ?? [],
169+
};
170+
}
171+
172+
function startWork(_issue: SearchedIssue) {
173+
// TODO: Hack here
174+
}

0 commit comments

Comments
 (0)