Merge from vscode 1a81711a85e38ccf784110568ebf3784ab9094a5 (#9161)

* Merge from vscode 1a81711a85e38ccf784110568ebf3784ab9094a5

* small spacing fix
This commit is contained in:
Anthony Dresser
2020-02-15 00:43:09 -06:00
committed by GitHub
parent 74b89a0a85
commit 873c6a39fe
78 changed files with 1474 additions and 1011 deletions

View File

@@ -6,7 +6,7 @@
import { lstat, Stats } from 'fs';
import * as os from 'os';
import * as path from 'path';
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder } from 'vscode';
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env } from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import * as nls from 'vscode-nls';
import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions } from './api/git';
@@ -17,6 +17,7 @@ import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineC
import { fromGitUri, toGitUri, isGitUri } from './uri';
import { grep, isDescendant, pathEquals } from './util';
import { Log, LogLevel } from './log';
import { GitTimelineItem } from './timelineProvider';
const localize = nls.loadMessageBundle();
@@ -2331,23 +2332,47 @@ export class CommandCenter {
return result && result.stash;
}
@command('git.openDiff', { repository: false })
async openDiff(uri: Uri, lhs: string, rhs: string) {
@command('git.timeline.openDiff', { repository: false })
async timelineOpenDiff(item: TimelineItem, uri: Uri | undefined, _source: string) {
// eslint-disable-next-line eqeqeq
if (uri == null || !GitTimelineItem.is(item)) {
return undefined;
}
const basename = path.basename(uri.fsPath);
let title;
if ((lhs === 'HEAD' || lhs === '~') && rhs === '') {
if ((item.previousRef === 'HEAD' || item.previousRef === '~') && item.ref === '') {
title = `${basename} (Working Tree)`;
}
else if (lhs === 'HEAD' && rhs === '~') {
else if (item.previousRef === 'HEAD' && item.ref === '~') {
title = `${basename} (Index)`;
} else {
title = `${basename} (${lhs.endsWith('^') ? `${lhs.substr(0, 8)}^` : lhs.substr(0, 8)}) \u27f7 ${basename} (${rhs.endsWith('^') ? `${rhs.substr(0, 8)}^` : rhs.substr(0, 8)})`;
title = `${basename} (${item.shortPreviousRef}) \u27f7 ${basename} (${item.shortRef})`;
}
return commands.executeCommand('vscode.diff', toGitUri(uri, lhs), rhs === '' ? uri : toGitUri(uri, rhs), title);
return commands.executeCommand('vscode.diff', toGitUri(uri, item.previousRef), item.ref === '' ? uri : toGitUri(uri, item.ref), title);
}
@command('git.timeline.copyCommitId', { repository: false })
async timelineCopyCommitId(item: TimelineItem, _uri: Uri | undefined, _source: string) {
if (!GitTimelineItem.is(item)) {
return;
}
env.clipboard.writeText(item.ref);
}
@command('git.timeline.copyCommitMessage', { repository: false })
async timelineCopyCommitMessage(item: TimelineItem, _uri: Uri | undefined, _source: string) {
if (!GitTimelineItem.is(item)) {
return;
}
env.clipboard.writeText(item.message);
}
private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any {
const result = (...args: any[]) => {
let result: Promise<any>;

View File

@@ -5,19 +5,62 @@
import * as dayjs from 'dayjs';
import * as advancedFormat from 'dayjs/plugin/advancedFormat';
import * as relativeTime from 'dayjs/plugin/relativeTime';
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, TimelineItem, TimelineProvider, Uri, workspace, TimelineChangeEvent } from 'vscode';
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineCursor, TimelineItem, TimelineProvider, Uri, workspace } from 'vscode';
import { Model } from './model';
import { Repository } from './repository';
import { debounce } from './decorators';
import { Status } from './api/git';
dayjs.extend(advancedFormat);
dayjs.extend(relativeTime);
// TODO[ECA]: Localize all the strings
// TODO[ECA]: Localize or use a setting for date format
export class GitTimelineItem extends TimelineItem {
static is(item: TimelineItem): item is GitTimelineItem {
return item instanceof GitTimelineItem;
}
readonly ref: string;
readonly previousRef: string;
readonly message: string;
constructor(
ref: string,
previousRef: string,
message: string,
timestamp: number,
id: string,
contextValue: string
) {
const index = message.indexOf('\n');
const label = index !== -1 ? `${message.substring(0, index)} \u2026` : message;
super(label, timestamp);
this.ref = ref;
this.previousRef = previousRef;
this.message = message;
this.id = id;
this.contextValue = contextValue;
}
get shortRef() {
return this.shortenRef(this.ref);
}
get shortPreviousRef() {
return this.shortenRef(this.previousRef);
}
private shortenRef(ref: string): string {
if (ref === '' || ref === '~' || ref === 'HEAD') {
return ref;
}
return ref.endsWith('^') ? `${ref.substr(0, 8)}^` : ref.substr(0, 8);
}
}
export class GitTimelineProvider implements TimelineProvider {
private _onDidChange = new EventEmitter<TimelineChangeEvent>();
get onDidChange(): Event<TimelineChangeEvent> {
@@ -44,7 +87,7 @@ export class GitTimelineProvider implements TimelineProvider {
this._disposable.dispose();
}
async provideTimeline(uri: Uri, _token: CancellationToken): Promise<TimelineItem[]> {
async provideTimeline(uri: Uri, _cursor: TimelineCursor, _token: CancellationToken): Promise<Timeline> {
// console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
const repo = this._model.getRepository(uri);
@@ -53,7 +96,7 @@ export class GitTimelineProvider implements TimelineProvider {
this._repoStatusDate = undefined;
this._repo = undefined;
return [];
return { items: [] };
}
if (this._repo?.root !== repo.root) {
@@ -72,25 +115,17 @@ export class GitTimelineProvider implements TimelineProvider {
const commits = await repo.logFile(uri);
let dateFormatter: dayjs.Dayjs;
const items = commits.map<TimelineItem>(c => {
let message = c.message;
const index = message.indexOf('\n');
if (index !== -1) {
message = `${message.substring(0, index)} \u2026`;
}
const items = commits.map<GitTimelineItem>(c => {
dateFormatter = dayjs(c.authorDate);
const item = new TimelineItem(message, c.authorDate?.getTime() ?? 0);
item.id = c.hash;
const item = new GitTimelineItem(c.hash, `${c.hash}^`, c.message, c.authorDate?.getTime() ?? 0, c.hash, 'git:file:commit');
item.iconPath = new (ThemeIcon as any)('git-commit');
item.description = `${dateFormatter.fromNow()} \u2022 ${c.authorName}`;
item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n\n${c.message}`;
item.description = c.authorName;
item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n\n${c.message}`;
item.command = {
title: 'Open Diff',
command: 'git.openDiff',
arguments: [uri, `${c.hash}^`, c.hash]
title: 'Open Comparison',
command: 'git.timeline.openDiff',
arguments: [uri, this.id, item]
};
return item;
@@ -123,16 +158,15 @@ export class GitTimelineProvider implements TimelineProvider {
break;
}
const item = new TimelineItem('Staged Changes', date.getTime());
item.id = 'index';
const item = new GitTimelineItem('~', 'HEAD', 'Staged Changes', date.getTime(), 'index', 'git:file:index');
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
item.iconPath = new (ThemeIcon as any)('git-commit');
item.description = `${dateFormatter.fromNow()} \u2022 You`;
item.detail = `You \u2014 Index\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`;
item.description = 'You';
item.detail = `You \u2014 Index\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`;
item.command = {
title: 'Open Comparison',
command: 'git.openDiff',
arguments: [uri, 'HEAD', '~']
command: 'git.timeline.openDiff',
arguments: [uri, this.id, item]
};
items.push(item);
@@ -166,22 +200,21 @@ export class GitTimelineProvider implements TimelineProvider {
break;
}
const item = new TimelineItem('Uncommited Changes', date.getTime());
item.id = 'working';
const item = new GitTimelineItem('', index ? '~' : 'HEAD', 'Uncommited Changes', date.getTime(), 'working', 'git:file:working');
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
item.iconPath = new (ThemeIcon as any)('git-commit');
item.description = `${dateFormatter.fromNow()} \u2022 You`;
item.detail = `You \u2014 Working Tree\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`;
item.description = 'You';
item.detail = `You \u2014 Working Tree\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`;
item.command = {
title: 'Open Comparison',
command: 'git.openDiff',
arguments: [uri, index ? '~' : 'HEAD', '']
command: 'git.timeline.openDiff',
arguments: [uri, this.id, item]
};
items.push(item);
}
return items;
return { items: items };
}
private onRepositoriesChanged(_repo: Repository) {
@@ -208,6 +241,6 @@ export class GitTimelineProvider implements TimelineProvider {
@debounce(500)
private fireChanged() {
this._onDidChange.fire();
this._onDidChange.fire({});
}
}