Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreadman committed Oct 21, 2023
2 parents 96539c3 + 4e8de8f commit 69b30db
Show file tree
Hide file tree
Showing 60 changed files with 1,012 additions and 151 deletions.
1 change: 1 addition & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"version": "2.0.0",
"tasks": [

{
"type": "npm",
"script": "watch-clientd",
Expand Down
4 changes: 4 additions & 0 deletions build/lib/i18n.resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,10 @@
{
"name": "vs/workbench/services/secrets",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/accountEntitlements",
"project": "vscode-workbench"
}
]
}
3 changes: 2 additions & 1 deletion extensions/git/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"scmValidation",
"tabInputTextMerge",
"timeline",
"contribMergeEditorMenus"
"contribMergeEditorMenus",
"scmInputBoxActionButton"
],
"categories": [
"Other"
Expand Down
6 changes: 5 additions & 1 deletion extensions/git/src/api/api1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions } from './git';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions, CommitMessageProvider } from './git';
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode';
import { combinedDisposable, mapEvent } from '../util';
import { toGitUri } from '../uri';
Expand Down Expand Up @@ -341,6 +341,10 @@ export class ApiImpl implements API {
return this._model.registerBranchProtectionProvider(root, provider);
}

registerCommitMessageProvider(provider: CommitMessageProvider): Disposable {
return this._model.registerCommitMessageProvider(provider);
}

constructor(private _model: Model) { }
}

Expand Down
9 changes: 8 additions & 1 deletion extensions/git/src/api/git.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Uri, Event, Disposable, ProviderResult, Command, CancellationToken } from 'vscode';
import { Uri, Event, Disposable, ProviderResult, Command, CancellationToken, ThemeIcon } from 'vscode';
export { ProviderResult } from 'vscode';

export interface Git {
Expand Down Expand Up @@ -300,6 +300,12 @@ export interface BranchProtectionProvider {
provideBranchProtection(): BranchProtection[];
}

export interface CommitMessageProvider {
readonly title: string;
readonly icon?: Uri | { light: Uri, dark: Uri } | ThemeIcon;
provideCommitMessage(repository: Repository, changes: string[], cancellationToken?: CancellationToken): Promise<string | undefined>;
}

export type APIState = 'uninitialized' | 'initialized';

export interface PublishEvent {
Expand Down Expand Up @@ -327,6 +333,7 @@ export interface API {
registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable;
registerPushErrorHandler(handler: PushErrorHandler): Disposable;
registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable;
registerCommitMessageProvider(provider: CommitMessageProvider): Disposable;
}

export interface GitExtension {
Expand Down
20 changes: 20 additions & 0 deletions extensions/git/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3581,6 +3581,26 @@ export class CommandCenter {
}
}

@command('git.generateCommitMessage', { repository: true })
async generateCommitMessage(repository: Repository): Promise<void> {
if (!repository || !this.model.commitMessageProvider) {
return;
}

await window.withProgress({ location: ProgressLocation.SourceControl }, async () => {
await repository.generateCommitMessage();
});
}

@command('git.generateCommitMessageCancel', { repository: true })
generateCommitMessageCancel(repository: Repository): void {
if (!repository || !this.model.commitMessageProvider) {
return;
}

repository.generateCommitMessageCancel();
}

private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any {
const result = (...args: any[]) => {
let result: Promise<any>;
Expand Down
154 changes: 154 additions & 0 deletions extensions/git/src/commitMessageProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { CancellationToken, Disposable, Event, EventEmitter, Uri, workspace, SourceControlInputBoxActionButton, ThemeIcon, l10n } from 'vscode';
import { CommitMessageProvider, Status, Repository as ApiRepository } from './api/git';
import { Repository } from './repository';
import { dispose } from './util';

export interface ICommitMessageProviderRegistry {
readonly onDidChangeCommitMessageProvider: Event<void>;

commitMessageProvider: CommitMessageProvider | undefined;
registerCommitMessageProvider(provider: CommitMessageProvider): Disposable;
}

export class TestCommitMessageProvider implements CommitMessageProvider {

readonly icon = new ThemeIcon('rocket');
readonly title = 'Generate Commit Message (Test)';

async provideCommitMessage(_: ApiRepository, __: string[], token: CancellationToken): Promise<string | undefined> {
if (token.isCancellationRequested) {
return undefined;
}

return new Promise(resolve => {
token.onCancellationRequested(() => resolve(undefined));
setTimeout(() => resolve(`Test commit message (${Math.random()})`), 5000);
});
}
}

interface ActionButtonState {
readonly isGenerating: boolean;
readonly enabled: boolean;
}

export class GenerateCommitMessageActionButton {

private _onDidChange = new EventEmitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }

private _state: ActionButtonState;
get state() { return this._state; }
set state(state: ActionButtonState) {
if (this._state.enabled === state.enabled &&
this._state.isGenerating === state.isGenerating) {
return;
}

this._state = state;
this._onDidChange.fire();
}

get button(): SourceControlInputBoxActionButton | undefined {
if (this.commitMessageProviderRegistry.commitMessageProvider === undefined) {
return undefined;
}

return this.state.isGenerating ?
{
icon: new ThemeIcon('debug-stop'),
command: { title: l10n.t('Cancel'), command: 'git.generateCommitMessageCancel' },
enabled: this.state.enabled
} :
{
icon: this.commitMessageProviderRegistry.commitMessageProvider.icon ?? new ThemeIcon('sparkle'),
command: { title: this.commitMessageProviderRegistry.commitMessageProvider.title, command: 'git.generateCommitMessage' },
enabled: this.state.enabled
};
}

private disposables: Disposable[] = [];

constructor(
private readonly repository: Repository,
private readonly commitMessageProviderRegistry: ICommitMessageProviderRegistry
) {
this._state = {
enabled: false,
isGenerating: false
};

const root = Uri.file(repository.root);
this.disposables.push(workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('git.enableSmartCommit', root) ||
e.affectsConfiguration('git.smartCommitChanges', root) ||
e.affectsConfiguration('git.suggestSmartCommit', root)) {
this.onDidChangeSmartCommitSettings();
}
}));
repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables);
repository.onDidStartCommitMessageGeneration(this.onDidStartCommitMessageGeneration, this, this.disposables);
repository.onDidEndCommitMessageGeneration(this.onDidEndCommitMessageGeneration, this, this.disposables);
commitMessageProviderRegistry.onDidChangeCommitMessageProvider(this.onDidChangeCommitMessageProvider, this, this.disposables);
}

private onDidChangeCommitMessageProvider(): void {
this._onDidChange.fire();
}

private onDidStartCommitMessageGeneration(): void {
this.state = { ...this.state, isGenerating: true };
}

private onDidEndCommitMessageGeneration(): void {
this.state = { ...this.state, isGenerating: false };
}

private onDidChangeSmartCommitSettings(): void {
this.state = {
...this.state,
enabled: this.repositoryHasChangesToCommit()
};
}

private onDidRunGitStatus(): void {
this.state = {
...this.state,
enabled: this.repositoryHasChangesToCommit()
};
}

private repositoryHasChangesToCommit(): boolean {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const enableSmartCommit = config.get<boolean>('enableSmartCommit') === true;
const suggestSmartCommit = config.get<boolean>('suggestSmartCommit') === true;
const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges', 'all');

const resources = [...this.repository.indexGroup.resourceStates];

if (
// Smart commit enabled (all)
(enableSmartCommit && smartCommitChanges === 'all') ||
// Smart commit disabled, smart suggestion enabled
(!enableSmartCommit && suggestSmartCommit)
) {
resources.push(...this.repository.workingTreeGroup.resourceStates);
}

// Smart commit enabled (tracked only)
if (enableSmartCommit && smartCommitChanges === 'tracked') {
resources.push(...this.repository.workingTreeGroup.resourceStates.filter(r => r.type !== Status.UNTRACKED));
}

return resources.length !== 0;
}

dispose(): void {
this.disposables = dispose(this.disposables);
}
}
25 changes: 22 additions & 3 deletions extensions/git/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import { Git } from './git';
import * as path from 'path';
import * as fs from 'fs';
import { fromGitUri } from './uri';
import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider } from './api/git';
import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider, CommitMessageProvider } from './api/git';
import { Askpass } from './askpass';
import { IPushErrorHandlerRegistry } from './pushError';
import { ApiRepository } from './api/api1';
import { IRemoteSourcePublisherRegistry } from './remotePublisher';
import { IPostCommitCommandsProviderRegistry } from './postCommitCommands';
import { IBranchProtectionProviderRegistry } from './branchProtection';
import { ICommitMessageProviderRegistry } from './commitMessageProvider';

class RepositoryPick implements QuickPickItem {
@memoize get label(): string {
Expand Down Expand Up @@ -170,7 +171,7 @@ class UnsafeRepositoriesManager {
}
}

export class Model implements IRepositoryResolver, IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry {
export class Model implements IRepositoryResolver, IBranchProtectionProviderRegistry, ICommitMessageProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry {

private _onDidOpenRepository = new EventEmitter<Repository>();
readonly onDidOpenRepository: Event<Repository> = this._onDidOpenRepository.event;
Expand Down Expand Up @@ -237,6 +238,14 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi

private pushErrorHandlers = new Set<PushErrorHandler>();

private _commitMessageProvider: CommitMessageProvider | undefined;
get commitMessageProvider(): CommitMessageProvider | undefined {
return this._commitMessageProvider;
}

private _onDidChangeCommitMessageProvider = new EventEmitter<void>();
readonly onDidChangeCommitMessageProvider = this._onDidChangeCommitMessageProvider.event;

private _unsafeRepositoriesManager: UnsafeRepositoriesManager;
get unsafeRepositories(): string[] {
return this._unsafeRepositoriesManager.repositories;
Expand Down Expand Up @@ -578,7 +587,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi

// Open repository
const [dotGit, repositoryRootRealPath] = await Promise.all([this.git.getRepositoryDotGit(repositoryRoot), this.getRepositoryRootRealPath(repositoryRoot)]);
const repository = new Repository(this.git.open(repositoryRoot, repositoryRootRealPath, dotGit, this.logger), this, this, this, this, this, this.globalState, this.logger, this.telemetryReporter);
const repository = new Repository(this.git.open(repositoryRoot, repositoryRootRealPath, dotGit, this.logger), this, this, this, this, this, this, this.globalState, this.logger, this.telemetryReporter);

this.open(repository);
this._closedRepositoriesManager.deleteRepository(repository.root);
Expand Down Expand Up @@ -939,6 +948,16 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi
return toDisposable(() => this.pushErrorHandlers.delete(handler));
}

registerCommitMessageProvider(provider: CommitMessageProvider): Disposable {
this._commitMessageProvider = provider;
this._onDidChangeCommitMessageProvider.fire();

return toDisposable(() => {
this._commitMessageProvider = undefined;
this._onDidChangeCommitMessageProvider.fire();
});
}

getPushErrorHandlers(): PushErrorHandler[] {
return [...this.pushErrorHandlers];
}
Expand Down
Loading

0 comments on commit 69b30db

Please sign in to comment.