Преглед на файлове

Closes #373 - adds better co-author support

main
Eric Amodio преди 5 години
родител
ревизия
2384a64fd1
променени са 8 файла, в които са добавени 251 реда и са изтрити 31 реда
  1. +4
    -0
      CHANGELOG.md
  2. +4
    -0
      images/dark/icon-person-add.svg
  3. +4
    -0
      images/light/icon-person-add.svg
  4. +37
    -5
      package.json
  5. +172
    -0
      src/commands/git/coauthors.ts
  6. +3
    -0
      src/commands/gitCommands.ts
  7. +15
    -0
      src/quickpicks/gitQuickPicks.ts
  8. +12
    -26
      src/views/viewCommands.ts

+ 4
- 0
CHANGELOG.md Целия файл

@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Added
- Adds better co-author support — closes [#373](https://github.com/eamodio/vscode-gitlens/issues/373)
- Adds a new _co-author_ command to the _Git Commands_ quick pick menu to add a co-author to a commit message
- Adds a new _Add Co-authors_ command to the inline toolbar and context menu for the _Contributors_ node in the _Repositories_ view
- Adds the _Add as Co-author_ command to the inline toolbar for the contributors in the _Repositories_ view
- Adds new actions options to the status bar blame
- Adds a `gitlens.showCommitsInView` option to show the commit in the _Search Commits_ view
- Adds a `gitlens.revealCommitInView` option to to reveal the commit in the _Repositories_ view

+ 4
- 0
images/dark/icon-person-add.svg Целия файл

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16">
<path stroke="#C5C5C5" d="M11.5 5v7M8 8.5h7"/>
<path fill="#C5C5C5" fill-rule="evenodd" d="M5 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm1 0a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm-4 7V7h4v3l-1 1v3H3v-3l-1-1zm4 1v3l-1 1H3l-1-1v-3l-1-1V7l1-1h4l1 1v3l-1 1z" clip-rule="evenodd"/>
</svg>

+ 4
- 0
images/light/icon-person-add.svg Целия файл

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16">
<path stroke="#424242" d="M11.5 5v7M8 8.5h7"/>
<path fill="#424242" fill-rule="evenodd" d="M5 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm1 0a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm-4 7V7h4v3l-1 1v3H3v-3l-1-1zm4 1v3l-1 1H3l-1-1v-3l-1-1V7l1-1h4l1 1v3l-1 1z" clip-rule="evenodd"/>
</svg>

+ 37
- 5
package.json Целия файл

@ -2839,9 +2839,22 @@
}
},
{
"command": "gitlens.views.contributor.addCoauthoredBy",
"command": "gitlens.views.contributors.addAuthors",
"title": "Add Co-authors",
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-person-add.svg",
"light": "images/light/icon-person-add.svg"
}
},
{
"command": "gitlens.views.contributor.addAuthor",
"title": "Add as Co-author",
"category": "GitLens"
"category": "GitLens",
"icon": {
"dark": "images/dark/icon-person-add.svg",
"light": "images/light/icon-person-add.svg"
}
},
{
"command": "gitlens.views.contributor.copyToClipboard",
@ -3817,7 +3830,11 @@
"when": "false"
},
{
"command": "gitlens.views.contributor.addCoauthoredBy",
"command": "gitlens.views.contributors.addAuthors",
"when": "false"
},
{
"command": "gitlens.views.contributor.addAuthor",
"when": "false"
},
{
@ -4737,9 +4754,24 @@
"group": "8_gitlens_actions@1"
},
{
"command": "gitlens.views.contributors.addAuthors",
"when": "viewItem =~ /gitlens:contributors\\b/",
"group": "inline@1"
},
{
"command": "gitlens.views.contributors.addAuthors",
"when": "viewItem =~ /gitlens:contributors\\b/",
"group": "1_gitlens_actions@1"
},
{
"command": "gitlens.inviteToLiveShare",
"when": "gitlens:vsls && gitlens:vsls != guest && viewItem =~ /gitlens:contributor\\b/",
"group": "inline@97"
"group": "inline@1"
},
{
"command": "gitlens.views.contributor.addAuthor",
"when": "viewItem =~ /gitlens:contributor\\b/",
"group": "inline@2"
},
{
"command": "gitlens.views.contributor.copyToClipboard",
@ -4752,7 +4784,7 @@
"group": "1_gitlens_actions@1"
},
{
"command": "gitlens.views.contributor.addCoauthoredBy",
"command": "gitlens.views.contributor.addAuthor",
"when": "viewItem =~ /gitlens:contributor\\b/",
"group": "1_gitlens_actions@2"
},

+ 172
- 0
src/commands/git/coauthors.ts Целия файл

@ -0,0 +1,172 @@
/* eslint-disable no-loop-func */
'use strict';
import { Container } from '../../container';
import { GitContributor, GitService, Repository } from '../../git/gitService';
import { QuickCommandBase, StepAsyncGenerator, StepSelection, StepState } from '../quickCommand';
import { ContributorQuickPickItem, Directive, DirectiveQuickPickItem, RepositoryQuickPickItem } from '../../quickpicks';
import { Logger } from '../../logger';
import { Strings } from '../../system';
interface State {
repo: Repository;
contributors: GitContributor[];
}
export interface CoAuthorsGitCommandArgs {
readonly command: 'co-authors';
state?: Partial<State>;
confirm?: boolean;
}
export class CoAuthorsGitCommand extends QuickCommandBase<State> {
constructor(args?: CoAuthorsGitCommandArgs) {
super('co-authors', 'co-authors', 'Add Co-Authors', { description: 'adds co-authors to a commit message' });
if (args == null || args.state === undefined) return;
let counter = 0;
if (args.state.repo !== undefined) {
counter++;
}
if (args.state.contributors !== undefined) {
counter++;
}
this._initialState = {
counter: counter,
confirm: args.confirm,
...args.state
};
}
get canConfirm() {
return false;
}
get hidden(): boolean {
return true;
}
async execute(state: State) {
const gitApi = await GitService.getBuiltInGitApi();
if (gitApi === undefined) return;
const repo = gitApi.repositories.find(r => Strings.normalizePath(r.rootUri.fsPath) === state.repo.path);
if (repo === undefined) return;
for (const c of state.contributors) {
const coauthor = `${c.name}${c.email ? ` <${c.email}>` : ''}`;
const message = repo.inputBox.value;
if (message.includes(coauthor)) continue;
let newlines;
if (message.includes('Co-authored-by: ')) {
newlines = '\n';
} else if (message.length !== 0 && message.endsWith('\n')) {
newlines = '\n\n';
} else {
newlines = '\n\n\n';
}
repo.inputBox.value = `${message}${newlines}Co-authored-by: ${coauthor}`;
}
}
protected async *steps(): StepAsyncGenerator {
const state: StepState<State> = this._initialState === undefined ? { counter: 0 } : this._initialState;
let activeRepo: Repository | undefined;
let repos;
while (true) {
try {
if (repos === undefined) {
repos = [...(await Container.git.getOrderedRepositories())];
const gitApi = await GitService.getBuiltInGitApi();
if (gitApi !== undefined) {
// Filter out any repo's that are not known to the built-in git
repos = repos.filter(repo =>
gitApi.repositories.find(r => Strings.normalizePath(r.rootUri.fsPath) === repo.path)
);
// Ensure that the active repo is known to the built-in git
activeRepo = await Container.git.getActiveRepository();
if (
activeRepo !== undefined &&
!gitApi.repositories.some(r => r.rootUri.fsPath === activeRepo!.path)
) {
activeRepo = undefined;
}
}
}
if (state.repo === undefined || !repos.includes(state.repo) || state.counter < 1) {
if (repos.length === 1) {
state.counter++;
state.repo = repos[0];
} else {
const active = state.repo ? state.repo : await Container.git.getActiveRepository();
const step = this.createPickStep<RepositoryQuickPickItem>({
title: this.title,
placeholder: 'Choose a repository',
items:
repos.length === 0
? [DirectiveQuickPickItem.create(Directive.Cancel)]
: await Promise.all(
repos.map(r =>
RepositoryQuickPickItem.create(r, r.id === (active && active.id), {
branch: true,
fetched: true,
status: true
})
)
)
});
const selection: StepSelection<typeof step> = yield step;
if (!this.canPickStepMoveNext(step, state, selection)) {
break;
}
state.repo = selection[0].item;
}
}
if (state.contributors === undefined || state.counter < 2) {
const step = this.createPickStep<ContributorQuickPickItem>({
title: `${this.title} to ${state.repo.formattedName}`,
multiselect: true,
placeholder: 'Choose contributors to add as co-authors',
matchOnDescription: true,
items: (await Container.git.getContributors(state.repo.path)).map(c =>
ContributorQuickPickItem.create(c)
)
});
const selection: StepSelection<typeof step> = yield step;
if (!this.canPickStepMoveNext(step, state, selection)) {
if (repos.length === 1) {
break;
}
continue;
}
state.contributors = selection.map(i => i.item);
}
await this.execute(state as State);
break;
} catch (ex) {
Logger.error(ex, this.title);
throw ex;
}
}
return undefined;
}
}

+ 3
- 0
src/commands/gitCommands.ts Целия файл

@ -21,6 +21,7 @@ import {
} from './quickCommand';
import { Directive, DirectiveQuickPickItem } from '../quickpicks';
import { CherryPickGitCommand, CherryPickGitCommandArgs } from './git/cherry-pick';
import { CoAuthorsGitCommand, CoAuthorsGitCommandArgs } from './git/coauthors';
import { FetchGitCommand, FetchGitCommandArgs } from './git/fetch';
import { MergeGitCommand, MergeGitCommandArgs } from './git/merge';
import { PullGitCommand, PullGitCommandArgs } from './git/pull';
@ -39,6 +40,7 @@ const sanitizeLabel = /\$\(.+?\)|\s/g;
export type GitCommandsCommandArgs =
| CherryPickGitCommandArgs
| CoAuthorsGitCommandArgs
| FetchGitCommandArgs
| MergeGitCommandArgs
| PullGitCommandArgs
@ -593,6 +595,7 @@ class PickCommandStep implements QuickPickStep {
constructor(args?: GitCommandsCommandArgs) {
this.items = [
new CherryPickGitCommand(args && args.command === 'cherry-pick' ? args : undefined),
new CoAuthorsGitCommand(args && args.command === 'co-authors' ? args : undefined),
new MergeGitCommand(args && args.command === 'merge' ? args : undefined),
new FetchGitCommand(args && args.command === 'fetch' ? args : undefined),
new PullGitCommand(args && args.command === 'pull' ? args : undefined),

+ 15
- 0
src/quickpicks/gitQuickPicks.ts Целия файл

@ -4,6 +4,7 @@ import { GlyphChars } from '../constants';
import { Dates, Strings } from '../system';
import {
GitBranch,
GitContributor,
GitLogCommit,
GitReference,
GitRemoteType,
@ -233,6 +234,20 @@ export namespace CommitQuickPickItem {
}
}
export interface ContributorQuickPickItem extends QuickPickItemOfT<GitContributor> {}
export namespace ContributorQuickPickItem {
export function create(contributor: GitContributor, picked?: boolean, options: {} = {}): ContributorQuickPickItem {
const item: ContributorQuickPickItem = {
label: contributor.name,
description: contributor.email,
picked: picked,
item: contributor
};
return item;
}
}
export interface RefQuickPickItem extends QuickPickItemOfT<GitReference> {
readonly current: boolean;
readonly ref: string;

+ 12
- 26
src/views/viewCommands.ts Целия файл

@ -22,6 +22,7 @@ import {
CompareBranchNode,
CompareResultsNode,
ContributorNode,
ContributorsNode,
FileHistoryNode,
FolderNode,
LineHistoryNode,
@ -40,7 +41,7 @@ import {
ViewRefNode,
viewSupportsNodeDismissal
} from './nodes';
import { debug, Strings } from '../system';
import { debug } from '../system';
import { runGitCommandInTerminal } from '../terminal';
interface CompareSelectedInfo {
@ -105,7 +106,8 @@ export class ViewCommands {
commands.registerCommand('gitlens.views.exploreRepoAtRevision', this.exploreRepoAtRevision, this);
commands.registerCommand('gitlens.views.contributor.addCoauthoredBy', this.contributorAddCoAuthoredBy, this);
commands.registerCommand('gitlens.views.contributors.addAuthors', this.contributorsAddAuthors, this);
commands.registerCommand('gitlens.views.contributor.addAuthor', this.contributorsAddAuthors, this);
commands.registerCommand('gitlens.views.contributor.copyToClipboard', this.contributorCopyToClipboard, this);
commands.registerCommand('gitlens.views.openChanges', this.openChanges, this);
@ -232,32 +234,16 @@ export class ViewCommands {
}
@debug()
private async contributorAddCoAuthoredBy(node: ContributorNode) {
if (!(node instanceof ContributorNode)) return;
const gitApi = await GitService.getBuiltInGitApi();
if (gitApi === undefined) return;
const repo = gitApi.repositories.find(
r => Strings.normalizePath(r.rootUri.fsPath) === node.contributor.repoPath
);
if (repo === undefined) return;
const coauthor = `${node.contributor.name}${node.contributor.email ? ` <${node.contributor.email}>` : ''}`;
private async contributorsAddAuthors(node: ContributorNode | ContributorsNode) {
if (!(node instanceof ContributorNode) && !(node instanceof ContributorsNode)) return undefined;
const message = repo.inputBox.value;
if (message.includes(coauthor)) return;
const repo = await Container.git.getRepository(node.uri.repoPath!);
let newlines;
if (message.includes('Co-authored-by: ')) {
newlines = '\n';
} else if (message.length !== 0 && message.endsWith('\n')) {
newlines = '\n\n';
} else {
newlines = '\n\n\n';
}
repo.inputBox.value = `${message}${newlines}Co-authored-by: ${coauthor}`;
const args: GitCommandsCommandArgs = {
command: 'co-authors',
state: { repo: repo, contributors: node instanceof ContributorNode ? [node.contributor] : undefined }
};
return commands.executeCommand(Commands.GitCommands, args);
}
@debug()

Зареждане…
Отказ
Запис