Enables typescript strict mode

Fixes all the compile/lint issues
This commit is contained in:
Eric Amodio
2017-05-11 02:14:58 -04:00
parent 90245b1111
commit ee29596d45
52 changed files with 525 additions and 461 deletions

View File

@@ -6,7 +6,7 @@ import { BuiltInCommands } from './constants';
export class ActiveEditorTracker extends Disposable { export class ActiveEditorTracker extends Disposable {
private _disposable: Disposable; private _disposable: Disposable;
private _resolver: (value?: TextEditor | PromiseLike<TextEditor>) => void; private _resolver: ((value?: TextEditor | PromiseLike<TextEditor>) => void) | undefined;
constructor() { constructor() {
super(() => this.dispose()); super(() => this.dispose());
@@ -28,11 +28,11 @@ export class ActiveEditorTracker extends Disposable {
return this.wait(timeout); return this.wait(timeout);
} }
async close(): Promise<{}> { async close(): Promise<{} | undefined> {
return commands.executeCommand(BuiltInCommands.CloseActiveEditor); return commands.executeCommand(BuiltInCommands.CloseActiveEditor);
} }
async next(): Promise<{}> { async next(): Promise<{} | undefined> {
return commands.executeCommand(BuiltInCommands.NextEditor); return commands.executeCommand(BuiltInCommands.NextEditor);
} }

View File

@@ -54,38 +54,38 @@ export class BlameActiveLineController extends Disposable {
} }
private _onConfigurationChanged() { private _onConfigurationChanged() {
const config = workspace.getConfiguration().get<IConfig>(ExtensionKey); const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
let changed: boolean = false; let changed: boolean = false;
if (!Objects.areEquivalent(config.statusBar, this._config && this._config.statusBar)) { if (!Objects.areEquivalent(cfg.statusBar, this._config && this._config.statusBar)) {
changed = true; changed = true;
if (config.statusBar.enabled) { if (cfg.statusBar.enabled) {
this._statusBarItem = this._statusBarItem || window.createStatusBarItem(StatusBarAlignment.Right, 1000); this._statusBarItem = this._statusBarItem || window.createStatusBarItem(StatusBarAlignment.Right, 1000);
this._statusBarItem.command = config.statusBar.command; this._statusBarItem.command = cfg.statusBar.command;
} }
else if (!config.statusBar.enabled && this._statusBarItem) { else if (!cfg.statusBar.enabled && this._statusBarItem) {
this._statusBarItem.dispose(); this._statusBarItem.dispose();
this._statusBarItem = undefined; this._statusBarItem = undefined;
} }
} }
if (!Objects.areEquivalent(config.blame.annotation.activeLine, this._config && this._config.blame.annotation.activeLine)) { if (!Objects.areEquivalent(cfg.blame.annotation.activeLine, this._config && this._config.blame.annotation.activeLine)) {
changed = true; changed = true;
if (config.blame.annotation.activeLine !== 'off' && this._editor) { if (cfg.blame.annotation.activeLine !== 'off' && this._editor) {
this._editor.setDecorations(activeLineDecoration, []); this._editor.setDecorations(activeLineDecoration, []);
} }
} }
if (!Objects.areEquivalent(config.blame.annotation.activeLineDarkColor, this._config && this._config.blame.annotation.activeLineDarkColor) || if (!Objects.areEquivalent(cfg.blame.annotation.activeLineDarkColor, this._config && this._config.blame.annotation.activeLineDarkColor) ||
!Objects.areEquivalent(config.blame.annotation.activeLineLightColor, this._config && this._config.blame.annotation.activeLineLightColor)) { !Objects.areEquivalent(cfg.blame.annotation.activeLineLightColor, this._config && this._config.blame.annotation.activeLineLightColor)) {
changed = true; changed = true;
} }
this._config = config; this._config = cfg;
if (!changed) return; if (!changed) return;
let trackActiveLine = config.statusBar.enabled || config.blame.annotation.activeLine !== 'off'; let trackActiveLine = cfg.statusBar.enabled || cfg.blame.annotation.activeLine !== 'off';
if (trackActiveLine && !this._activeEditorLineDisposable) { if (trackActiveLine && !this._activeEditorLineDisposable) {
const subscriptions: Disposable[] = []; const subscriptions: Disposable[] = [];
@@ -103,8 +103,8 @@ export class BlameActiveLineController extends Disposable {
this._onActiveTextEditorChanged(window.activeTextEditor); this._onActiveTextEditorChanged(window.activeTextEditor);
} }
private isEditorBlameable(editor: TextEditor): boolean { private isEditorBlameable(editor: TextEditor | undefined): boolean {
if (!editor || !editor.document) return false; if (editor === undefined || editor.document === undefined) return false;
const scheme = editor.document.uri.scheme; const scheme = editor.document.uri.scheme;
if (scheme !== DocumentSchemes.File && scheme !== DocumentSchemes.Git && scheme !== DocumentSchemes.GitLensGit) return false; if (scheme !== DocumentSchemes.File && scheme !== DocumentSchemes.Git && scheme !== DocumentSchemes.GitLensGit) return false;
@@ -114,13 +114,13 @@ export class BlameActiveLineController extends Disposable {
return this.git.isEditorBlameable(editor); return this.git.isEditorBlameable(editor);
} }
private async _onActiveTextEditorChanged(editor: TextEditor) { private async _onActiveTextEditorChanged(editor: TextEditor | undefined) {
this._currentLine = -1; this._currentLine = -1;
const previousEditor = this._editor; const previousEditor = this._editor;
previousEditor && previousEditor.setDecorations(activeLineDecoration, []); previousEditor && previousEditor.setDecorations(activeLineDecoration, []);
if (!this.isEditorBlameable(editor)) { if (editor === undefined || !this.isEditorBlameable(editor)) {
this.clear(editor); this.clear(editor);
this._editor = undefined; this._editor = undefined;
@@ -128,7 +128,7 @@ export class BlameActiveLineController extends Disposable {
return; return;
} }
this._blameable = editor && editor.document && !editor.document.isDirty; this._blameable = editor !== undefined && editor.document !== undefined && !editor.document.isDirty;
this._editor = editor; this._editor = editor;
this._uri = await GitUri.fromUri(editor.document.uri, this.git); this._uri = await GitUri.fromUri(editor.document.uri, this.git);
const maxLines = this._config.advanced.caching.statusBar.maxLines; const maxLines = this._config.advanced.caching.statusBar.maxLines;
@@ -183,29 +183,29 @@ export class BlameActiveLineController extends Disposable {
private async _updateBlame(line: number, editor: TextEditor) { private async _updateBlame(line: number, editor: TextEditor) {
line = line - this._uri.offset; line = line - this._uri.offset;
let commit: GitCommit; let commit: GitCommit | undefined = undefined;
let commitLine: IGitCommitLine; let commitLine: IGitCommitLine | undefined = undefined;
// Since blame information isn't valid when there are unsaved changes -- don't show any status // Since blame information isn't valid when there are unsaved changes -- don't show any status
if (this._blameable && line >= 0) { if (this._blameable && line >= 0) {
if (this._useCaching) { if (this._useCaching) {
const blame = this._blame && await this._blame; const blame = this._blame && await this._blame;
if (!blame || !blame.lines.length) { if (blame === undefined || !blame.lines.length) {
this.clear(editor); this.clear(editor);
return; return;
} }
commitLine = blame.lines[line]; commitLine = blame.lines[line];
const sha = commitLine && commitLine.sha; const sha = commitLine === undefined ? undefined : commitLine.sha;
commit = sha && blame.commits.get(sha); commit = sha === undefined ? undefined : blame.commits.get(sha);
} }
else { else {
const blameLine = await this.git.getBlameForLine(this._uri, line); const blameLine = await this.git.getBlameForLine(this._uri, line);
commitLine = blameLine && blameLine.line; commitLine = blameLine === undefined ? undefined : blameLine.line;
commit = blameLine && blameLine.commit; commit = blameLine === undefined ? undefined : blameLine.commit;
} }
} }
if (commit) { if (commit !== undefined && commitLine !== undefined) {
this.show(commit, commitLine, editor); this.show(commit, commitLine, editor);
} }
else { else {
@@ -213,7 +213,7 @@ export class BlameActiveLineController extends Disposable {
} }
} }
clear(editor: TextEditor, previousEditor?: TextEditor) { clear(editor: TextEditor | undefined, previousEditor?: TextEditor) {
editor && editor.setDecorations(activeLineDecoration, []); editor && editor.setDecorations(activeLineDecoration, []);
// I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay // I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay
if (editor) { if (editor) {
@@ -227,7 +227,7 @@ export class BlameActiveLineController extends Disposable {
// I have no idea why I need this protection -- but it happens // I have no idea why I need this protection -- but it happens
if (!editor.document) return; if (!editor.document) return;
if (this._config.statusBar.enabled) { if (this._config.statusBar.enabled && this._statusBarItem !== undefined) {
switch (this._config.statusBar.date) { switch (this._config.statusBar.date) {
case 'off': case 'off':
this._statusBarItem.text = `$(git-commit) ${commit.author}`; this._statusBarItem.text = `$(git-commit) ${commit.author}`;
@@ -284,7 +284,7 @@ export class BlameActiveLineController extends Disposable {
const activeLine = this._config.blame.annotation.activeLine; const activeLine = this._config.blame.annotation.activeLine;
const offset = this._uri.offset; const offset = this._uri.offset;
const config = { const cfg = {
annotation: { annotation: {
sha: true, sha: true,
author: this._config.statusBar.enabled ? false : this._config.blame.annotation.author, author: this._config.statusBar.enabled ? false : this._config.blame.annotation.author,
@@ -293,10 +293,10 @@ export class BlameActiveLineController extends Disposable {
} }
} as IBlameConfig; } as IBlameConfig;
const annotation = BlameAnnotationFormatter.getAnnotation(config, commit, BlameAnnotationFormat.Unconstrained); const annotation = BlameAnnotationFormatter.getAnnotation(cfg, commit, BlameAnnotationFormat.Unconstrained);
// Get the full commit message -- since blame only returns the summary // Get the full commit message -- since blame only returns the summary
let logCommit: GitCommit; let logCommit: GitCommit | undefined = undefined;
if (!commit.isUncommitted) { if (!commit.isUncommitted) {
logCommit = await this.git.getLogCommit(this._uri.repoPath, this._uri.fsPath, commit.sha); logCommit = await this.git.getLogCommit(this._uri.repoPath, this._uri.fsPath, commit.sha);
} }
@@ -304,17 +304,17 @@ export class BlameActiveLineController extends Disposable {
// I have no idea why I need this protection -- but it happens // I have no idea why I need this protection -- but it happens
if (!editor.document) return; if (!editor.document) return;
let hoverMessage: string | string[]; let hoverMessage: string | string[] | undefined = undefined;
if (activeLine !== 'inline') { if (activeLine !== 'inline') {
// If the messages match (or we couldn't find the log), then this is a possible duplicate annotation // If the messages match (or we couldn't find the log), then this is a possible duplicate annotation
const possibleDuplicate = !logCommit || logCommit.message === commit.message; const possibleDuplicate = !logCommit || logCommit.message === commit.message;
// If we don't have a possible dupe or we aren't showing annotations get the hover message // If we don't have a possible dupe or we aren't showing annotations get the hover message
if (!commit.isUncommitted && (!possibleDuplicate || !this.annotationController.isAnnotating(editor))) { if (!commit.isUncommitted && (!possibleDuplicate || !this.annotationController.isAnnotating(editor))) {
hoverMessage = BlameAnnotationFormatter.getAnnotationHover(config, blameLine, logCommit || commit); hoverMessage = BlameAnnotationFormatter.getAnnotationHover(cfg, blameLine, logCommit || commit);
} }
} }
let decorationOptions: DecorationOptions; let decorationOptions: DecorationOptions | undefined = undefined;
switch (activeLine) { switch (activeLine) {
case 'both': case 'both':
case 'inline': case 'inline':
@@ -347,7 +347,9 @@ export class BlameActiveLineController extends Disposable {
break; break;
} }
decorationOptions && editor.setDecorations(activeLineDecoration, [decorationOptions]); if (decorationOptions !== undefined) {
editor.setDecorations(activeLineDecoration, [decorationOptions]);
}
} }
} }
} }

View File

@@ -18,7 +18,7 @@ export const BlameDecorations = {
margin: '0 0 0 4em' margin: '0 0 0 4em'
} }
} as DecorationRenderOptions), } as DecorationRenderOptions),
highlight: undefined as TextEditorDecorationType highlight: undefined as TextEditorDecorationType | undefined
}; };
export class BlameAnnotationController extends Disposable { export class BlameAnnotationController extends Disposable {
@@ -29,7 +29,7 @@ export class BlameAnnotationController extends Disposable {
} }
private _annotationProviders: Map<number, BlameAnnotationProvider> = new Map(); private _annotationProviders: Map<number, BlameAnnotationProvider> = new Map();
private _blameAnnotationsDisposable: Disposable; private _blameAnnotationsDisposable: Disposable | undefined;
private _config: IBlameConfig; private _config: IBlameConfig;
private _disposable: Disposable; private _disposable: Disposable;
private _whitespaceController: WhitespaceController | undefined; private _whitespaceController: WhitespaceController | undefined;
@@ -73,12 +73,12 @@ export class BlameAnnotationController extends Disposable {
this._whitespaceController = undefined; this._whitespaceController = undefined;
} }
const config = workspace.getConfiguration(ExtensionKey).get<IBlameConfig>('blame'); const cfg = workspace.getConfiguration(ExtensionKey).get<IBlameConfig>('blame')!;
if (config.annotation.highlight !== (this._config && this._config.annotation.highlight)) { if (cfg.annotation.highlight !== (this._config && this._config.annotation.highlight)) {
BlameDecorations.highlight && BlameDecorations.highlight.dispose(); BlameDecorations.highlight && BlameDecorations.highlight.dispose();
switch (config.annotation.highlight) { switch (cfg.annotation.highlight) {
case 'gutter': case 'gutter':
BlameDecorations.highlight = window.createTextEditorDecorationType({ BlameDecorations.highlight = window.createTextEditorDecorationType({
dark: { dark: {
@@ -133,7 +133,7 @@ export class BlameAnnotationController extends Disposable {
} }
} }
this._config = config; this._config = cfg;
} }
async clear(column: number) { async clear(column: number) {

View File

@@ -24,7 +24,7 @@ export class BlameAnnotationProvider extends Disposable {
this._blame = this.git.getBlameForFile(this.uri); this._blame = this.git.getBlameForFile(this.uri);
this._config = workspace.getConfiguration(ExtensionKey).get<IBlameConfig>('blame'); this._config = workspace.getConfiguration(ExtensionKey).get<IBlameConfig>('blame')!;
const subscriptions: Disposable[] = []; const subscriptions: Disposable[] = [];
@@ -39,8 +39,12 @@ export class BlameAnnotationProvider extends Disposable {
this.editor.setDecorations(BlameDecorations.annotation, []); this.editor.setDecorations(BlameDecorations.annotation, []);
BlameDecorations.highlight && this.editor.setDecorations(BlameDecorations.highlight, []); BlameDecorations.highlight && this.editor.setDecorations(BlameDecorations.highlight, []);
// I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay // I have no idea why the decorators sometimes don't get removed, but if they don't try again with a tiny delay
if (BlameDecorations.highlight) { if (BlameDecorations.highlight !== undefined) {
setTimeout(() => this.editor.setDecorations(BlameDecorations.highlight, []), 1); setTimeout(() => {
if (BlameDecorations.highlight === undefined) return;
this.editor.setDecorations(BlameDecorations.highlight, []);
}, 1);
} }
} }
catch (ex) { } catch (ex) { }
@@ -64,7 +68,7 @@ export class BlameAnnotationProvider extends Disposable {
} }
async provideBlameAnnotation(shaOrLine?: string | number): Promise<boolean> { async provideBlameAnnotation(shaOrLine?: string | number): Promise<boolean> {
let whitespacePromise: Promise<void>; let whitespacePromise: Promise<void> | undefined;
// HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- override whitespace (turn off) // HACK: Until https://github.com/Microsoft/vscode/issues/11485 is fixed -- override whitespace (turn off)
if (this._config.annotation.style !== BlameAnnotationStyle.Trailing) { if (this._config.annotation.style !== BlameAnnotationStyle.Trailing) {
whitespacePromise = this.whitespaceController && this.whitespaceController.override(); whitespacePromise = this.whitespaceController && this.whitespaceController.override();
@@ -116,7 +120,7 @@ export class BlameAnnotationProvider extends Disposable {
const offset = this.uri.offset; const offset = this.uri.offset;
let sha: string; let sha: string | undefined = undefined;
if (typeof shaOrLine === 'string') { if (typeof shaOrLine === 'string') {
sha = shaOrLine; sha = shaOrLine;
} }
@@ -149,7 +153,8 @@ export class BlameAnnotationProvider extends Disposable {
let count = 0; let count = 0;
let lastSha: string; let lastSha: string;
return blame.lines.map(l => { return blame.lines.map(l => {
let commit = blame.commits.get(l.sha); const commit = blame.commits.get(l.sha);
if (commit === undefined) throw new Error(`Cannot find sha ${l.sha}`);
let color: string; let color: string;
if (commit.isUncommitted) { if (commit.isUncommitted) {
@@ -248,7 +253,8 @@ export class BlameAnnotationProvider extends Disposable {
} }
return blame.lines.map(l => { return blame.lines.map(l => {
let commit = blame.commits.get(l.sha); const commit = blame.commits.get(l.sha);
if (commit === undefined) throw new Error(`Cannot find sha ${l.sha}`);
let color: string; let color: string;
if (commit.isUncommitted) { if (commit.isUncommitted) {

View File

@@ -33,11 +33,11 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
let active = window.activeTextEditor; let active = window.activeTextEditor;
let editor = active; let editor = active;
do { do {
if (editor) { if (editor !== undefined) {
if ((editor.document && editor.document.isDirty) || if ((editor.document !== undefined && editor.document.isDirty) ||
uris.some(_ => UriComparer.equals(_, editor.document && editor.document.uri))) { uris.some(_ => UriComparer.equals(_, editor!.document && editor!.document.uri))) {
// If we didn't start with a valid editor, set one once we find it // If we didn't start with a valid editor, set one once we find it
if (!active) { if (active === undefined) {
active = editor; active = editor;
} }
editor = await editorTracker.awaitNext(500); editor = await editorTracker.awaitNext(500);
@@ -55,7 +55,7 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
} }
editor = await editorTracker.awaitClose(500); editor = await editorTracker.awaitClose(500);
} }
} while ((!active && !editor) || !TextEditorComparer.equals(active, editor, { useId: true, usePosition: true })); } while ((active === undefined && editor === undefined) || !TextEditorComparer.equals(active, editor, { useId: true, usePosition: true }));
editorTracker.dispose(); editorTracker.dispose();

View File

@@ -59,7 +59,6 @@ export const CommandContext = {
Key: 'gitlens:key' as CommandContext Key: 'gitlens:key' as CommandContext
}; };
export function setCommandContext(key: CommandContext | string, value: any) { export function setCommandContext(key: CommandContext | string, value: any) {
return commands.executeCommand(BuiltInCommands.SetContext, key, value); return commands.executeCommand(BuiltInCommands.SetContext, key, value);
} }
@@ -119,7 +118,7 @@ export abstract class ActiveEditorCommand extends Command {
abstract execute(editor: TextEditor, ...args: any[]): any; abstract execute(editor: TextEditor, ...args: any[]): any;
} }
let lastCommand: { command: string, args: any[] } = undefined; let lastCommand: { command: string, args: any[] } | undefined = undefined;
export function getLastCommand() { export function getLastCommand() {
return lastCommand; return lastCommand;
} }

View File

@@ -32,6 +32,7 @@ export class DiffDirectoryCommand extends ActiveEditorCommand {
if (!shaOrBranch1) { if (!shaOrBranch1) {
const branches = await this.git.getBranches(repoPath); const branches = await this.git.getBranches(repoPath);
const current = Iterables.find(branches, _ => _.current); const current = Iterables.find(branches, _ => _.current);
if (current == null) return window.showWarningMessage(`Unable to open directory compare`);
const pick = await BranchesQuickPick.show(branches, `Compare ${current.name} to \u2026`); const pick = await BranchesQuickPick.show(branches, `Compare ${current.name} to \u2026`);
if (!pick) return undefined; if (!pick) return undefined;

View File

@@ -43,7 +43,7 @@ export class DiffLineWithPreviousCommand extends ActiveEditorCommand {
// If the line is uncommitted, find the previous commit and treat it as a DiffWithWorking // If the line is uncommitted, find the previous commit and treat it as a DiffWithWorking
if (commit.isUncommitted) { if (commit.isUncommitted) {
uri = commit.uri; uri = commit.uri;
commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha, commit.previousFileName, commit.author, commit.date, commit.message); commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha!, commit.previousFileName!, commit.author, commit.date, commit.message);
line = (blame.line.line + 1) + gitUri.offset; line = (blame.line.line + 1) + gitUri.offset;
return commands.executeCommand(Commands.DiffWithWorking, uri, commit, line); return commands.executeCommand(Commands.DiffWithWorking, uri, commit, line);
} }
@@ -56,10 +56,10 @@ export class DiffLineWithPreviousCommand extends ActiveEditorCommand {
try { try {
const [rhs, lhs] = await Promise.all([ const [rhs, lhs] = await Promise.all([
this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, gitUri.sha), this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, gitUri.sha!),
this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha) this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha)
]); ]);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) ${path.basename(gitUri.fsPath)} (${gitUri.shortSha})`); await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) \u2194 ${path.basename(gitUri.fsPath)} (${gitUri.shortSha})`);
// TODO: Figure out how to focus the left pane // TODO: Figure out how to focus the left pane
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' }); return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
} }

View File

@@ -34,7 +34,7 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
commit = blame.commit; commit = blame.commit;
// If the line is uncommitted, find the previous commit // If the line is uncommitted, find the previous commit
if (commit.isUncommitted) { if (commit.isUncommitted) {
commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha, commit.previousFileName, commit.author, commit.date, commit.message); commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha!, commit.previousFileName!, commit.author, commit.date, commit.message);
line = blame.line.line + 1 + gitUri.offset; line = blame.line.line + 1 + gitUri.offset;
} }
} }

View File

@@ -22,6 +22,7 @@ export class DiffWithBranchCommand extends ActiveEditorCommand {
const line = (editor && editor.selection.active.line) || 0; const line = (editor && editor.selection.active.line) || 0;
const gitUri = await GitUri.fromUri(uri, this.git); const gitUri = await GitUri.fromUri(uri, this.git);
if (gitUri.repoPath === undefined) return undefined;
const branches = await this.git.getBranches(gitUri.repoPath); const branches = await this.git.getBranches(gitUri.repoPath);
const pick = await BranchesQuickPick.show(branches, `Compare ${path.basename(gitUri.fsPath)} to \u2026`, goBackCommand); const pick = await BranchesQuickPick.show(branches, `Compare ${path.basename(gitUri.fsPath)} to \u2026`, goBackCommand);
@@ -36,7 +37,7 @@ export class DiffWithBranchCommand extends ActiveEditorCommand {
try { try {
const compare = await this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, branch); const compare = await this.git.getVersionedFile(gitUri.repoPath, gitUri.fsPath, branch);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), gitUri.fileUri(), `${path.basename(gitUri.fsPath)} (${branch}) ${path.basename(gitUri.fsPath)}`); await commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), gitUri.fileUri(), `${path.basename(gitUri.fsPath)} (${branch}) \u2194 ${path.basename(gitUri.fsPath)}`);
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' }); return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
} }
catch (ex) { catch (ex) {

View File

@@ -42,7 +42,7 @@ export class DiffWithNextCommand extends ActiveEditorCommand {
const sha = (commit && commit.sha) || gitUri.sha; const sha = (commit && commit.sha) || gitUri.sha;
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, rangeOrLine as Range); const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, rangeOrLine!);
if (!log) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`); if (!log) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values()); commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
@@ -62,7 +62,7 @@ export class DiffWithNextCommand extends ActiveEditorCommand {
this.git.getVersionedFile(commit.repoPath, commit.nextUri.fsPath, commit.nextSha), this.git.getVersionedFile(commit.repoPath, commit.nextUri.fsPath, commit.nextSha),
this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha) this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha)
]); ]);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) ${path.basename(commit.nextUri.fsPath)} (${commit.nextShortSha})`); await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) \u2194 ${path.basename(commit.nextUri.fsPath)} (${commit.nextShortSha})`);
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' }); return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
} }
catch (ex) { catch (ex) {

View File

@@ -43,7 +43,7 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
const sha = (commit && commit.sha) || gitUri.sha; const sha = (commit && commit.sha) || gitUri.sha;
const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, rangeOrLine as Range); const log = await this.git.getLogForFile(gitUri.repoPath, gitUri.fsPath, undefined, sha ? undefined : 2, rangeOrLine!);
if (!log) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`); if (!log) return window.showWarningMessage(`Unable to open compare. File is probably not under source control`);
commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values()); commit = (sha && log.commits.get(sha)) || Iterables.first(log.commits.values());
@@ -63,7 +63,7 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha), this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha),
this.git.getVersionedFile(commit.repoPath, commit.previousUri.fsPath, commit.previousSha) this.git.getVersionedFile(commit.repoPath, commit.previousUri.fsPath, commit.previousSha)
]); ]);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.previousUri.fsPath)} (${commit.previousShortSha}) ${path.basename(commit.uri.fsPath)} (${commit.shortSha})`); await commands.executeCommand(BuiltInCommands.Diff, Uri.file(lhs), Uri.file(rhs), `${path.basename(commit.previousUri.fsPath)} (${commit.previousShortSha}) \u2194 ${path.basename(commit.uri.fsPath)} (${commit.shortSha})`);
// TODO: Figure out how to focus the left pane // TODO: Figure out how to focus the left pane
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' }); return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
} }

View File

@@ -39,10 +39,11 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
const gitUri = await GitUri.fromUri(uri, this.git); const gitUri = await GitUri.fromUri(uri, this.git);
const workingFileName = await this.git.findWorkingFileName(gitUri.repoPath, gitUri.fsPath); const workingFileName = await this.git.findWorkingFileName(gitUri.repoPath, gitUri.fsPath);
if (workingFileName === undefined) return undefined;
try { try {
const compare = await this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha); const compare = await this.git.getVersionedFile(commit.repoPath, commit.uri.fsPath, commit.sha);
await commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), Uri.file(path.resolve(gitUri.repoPath, workingFileName)), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) ${path.basename(workingFileName)}`); await commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), Uri.file(path.resolve(gitUri.repoPath, workingFileName)), `${path.basename(commit.uri.fsPath)} (${commit.shortSha}) \u2194 ${path.basename(workingFileName)}`);
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' }); return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
} }
catch (ex) { catch (ex) {

View File

@@ -16,7 +16,7 @@ export const keys: Keys[] = [
'.' '.'
]; ];
export declare type KeyMapping = { [id: string]: (QuickPickItem | (() => Promise<QuickPickItem>)) }; export declare type KeyMapping = { [id: string]: (QuickPickItem | (() => Promise<QuickPickItem>) | undefined) };
let mappings: KeyMapping[] = []; let mappings: KeyMapping[] = [];
let _instance: Keyboard; let _instance: Keyboard;
@@ -113,7 +113,7 @@ export class Keyboard extends Disposable {
return await new KeyboardScope(mapping ? Object.assign(Object.create(null), mapping) : Object.create(null)).begin(); return await new KeyboardScope(mapping ? Object.assign(Object.create(null), mapping) : Object.create(null)).begin();
} }
async execute(key: Keys): Promise<{}> { async execute(key: Keys): Promise<{} | undefined> {
if (!mappings.length) return undefined; if (!mappings.length) return undefined;
try { try {

View File

@@ -34,7 +34,7 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
let commit = blame.commit; let commit = blame.commit;
// If the line is uncommitted, find the previous commit // If the line is uncommitted, find the previous commit
if (commit.isUncommitted) { if (commit.isUncommitted) {
commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha, commit.previousFileName, commit.author, commit.date, commit.message); commit = new GitCommit(commit.type, commit.repoPath, commit.previousSha!, commit.previousFileName!, commit.author, commit.date, commit.message);
} }
const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider); const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider);

View File

@@ -27,7 +27,7 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
try { try {
const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider); const remotes = Arrays.uniqueBy(await this.git.getRemotes(gitUri.repoPath), _ => _.url, _ => !!_.provider);
const range = editor && new Range(editor.selection.start.with({ line: editor.selection.start.line + 1 }), editor.selection.end.with({ line: editor.selection.end.line + 1 })); const range = editor && new Range(editor.selection.start.with({ line: editor.selection.start.line + 1 }), editor.selection.end.with({ line: editor.selection.end.line + 1 }));
return commands.executeCommand(Commands.OpenInRemote, uri, remotes, 'file', [gitUri.getRelativePath(), branch.name, gitUri.sha, range]); return commands.executeCommand(Commands.OpenInRemote, uri, remotes, 'file', [gitUri.getRelativePath(), branch === undefined ? 'Current' : branch.name, gitUri.sha, range]);
} }
catch (ex) { catch (ex) {
Logger.error(ex, 'OpenFileInRemoteCommand'); Logger.error(ex, 'OpenFileInRemoteCommand');

View File

@@ -11,20 +11,21 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
super(Commands.OpenInRemote); super(Commands.OpenInRemote);
} }
async execute(editor: TextEditor, uri?: Uri, remotes?: GitRemote[], type?: RemoteOpenType, args?: string[], goBackCommand?: CommandQuickPickItem) { async execute(editor: TextEditor, uri?: Uri, remotes?: GitRemote[], type?: RemoteOpenType, args: string[] = [], goBackCommand?: CommandQuickPickItem) {
if (!(uri instanceof Uri)) { if (!(uri instanceof Uri)) {
uri = editor && editor.document && editor.document.uri; uri = editor && editor.document && editor.document.uri;
} }
try { try {
if (!remotes) return undefined; if (remotes === undefined) return undefined;
if (type === undefined) throw new Error(`Invalid type ${type}`);
if (remotes.length === 1) { if (remotes.length === 1) {
const command = new OpenRemoteCommandQuickPickItem(remotes[0], type, ...args); const command = new OpenRemoteCommandQuickPickItem(remotes[0], type, ...args);
return command.execute(); return command.execute();
} }
let placeHolder: string; let placeHolder: string = '';
switch (type) { switch (type) {
case 'branch': case 'branch':
placeHolder = `open ${args[0]} branch in\u2026`; placeHolder = `open ${args[0]} branch in\u2026`;

View File

@@ -25,6 +25,7 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
} }
const gitUri = await GitUri.fromUri(uri, this.git); const gitUri = await GitUri.fromUri(uri, this.git);
if (gitUri.repoPath === undefined) return undefined;
if (!search || searchBy == null) { if (!search || searchBy == null) {
search = await window.showInputBox({ search = await window.showInputBox({
@@ -48,10 +49,15 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
} }
try { try {
const log = await this.git.getLogForRepoSearch(gitUri.repoPath, search, searchBy); if (searchBy === undefined) {
searchBy = GitRepoSearchBy.Message;
}
let originalSearch: string; const log = await this.git.getLogForRepoSearch(gitUri.repoPath, search, searchBy);
let placeHolder: string; if (log === undefined) return undefined;
let originalSearch: string | undefined = undefined;
let placeHolder: string | undefined = undefined;
switch (searchBy) { switch (searchBy) {
case GitRepoSearchBy.Author: case GitRepoSearchBy.Author:
originalSearch = `@${search}`; originalSearch = `@${search}`;
@@ -77,7 +83,7 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
description: `\u00a0 \u2014 \u00a0\u00a0 to commit search` description: `\u00a0 \u2014 \u00a0\u00a0 to commit search`
}, Commands.ShowCommitSearch, [gitUri, originalSearch, undefined, goBackCommand]); }, Commands.ShowCommitSearch, [gitUri, originalSearch, undefined, goBackCommand]);
const pick = await CommitsQuickPick.show(this.git, log, placeHolder, currentCommand); const pick = await CommitsQuickPick.show(this.git, log, placeHolder!, currentCommand);
if (!pick) return undefined; if (!pick) return undefined;
if (pick instanceof CommandQuickPickItem) { if (pick instanceof CommandQuickPickItem) {

View File

@@ -22,12 +22,12 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
maxCount = this.git.config.advanced.maxQuickHistory; maxCount = this.git.config.advanced.maxQuickHistory;
} }
let progressCancellation = branch && BranchHistoryQuickPick.showProgress(branch); let progressCancellation = branch === undefined ? undefined : BranchHistoryQuickPick.showProgress(branch);
try { try {
const repoPath = (gitUri && gitUri.repoPath) || this.git.repoPath; const repoPath = (gitUri && gitUri.repoPath) || this.git.repoPath;
if (!repoPath) return window.showWarningMessage(`Unable to show branch history`); if (repoPath === undefined) return window.showWarningMessage(`Unable to show branch history`);
if (!branch) { if (branch === undefined) {
const branches = await this.git.getBranches(repoPath); const branches = await this.git.getBranches(repoPath);
const pick = await BranchesQuickPick.show(branches, `Show history for branch\u2026`); const pick = await BranchesQuickPick.show(branches, `Show history for branch\u2026`);
@@ -38,7 +38,7 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
} }
branch = pick.branch.name; branch = pick.branch.name;
if (!branch) return undefined; if (branch === undefined) return undefined;
progressCancellation = BranchHistoryQuickPick.showProgress(branch); progressCancellation = BranchHistoryQuickPick.showProgress(branch);
} }
@@ -48,9 +48,9 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
if (!log) return window.showWarningMessage(`Unable to show branch history`); if (!log) return window.showWarningMessage(`Unable to show branch history`);
} }
if (progressCancellation.token.isCancellationRequested) return undefined; if (progressCancellation !== undefined && progressCancellation.token.isCancellationRequested) return undefined;
const pick = await BranchHistoryQuickPick.show(this.git, log, gitUri, branch, progressCancellation, goBackCommand, nextPageCommand); const pick = await BranchHistoryQuickPick.show(this.git, log, gitUri, branch, progressCancellation!, goBackCommand, nextPageCommand);
if (!pick) return undefined; if (!pick) return undefined;
if (pick instanceof CommandQuickPickItem) { if (pick instanceof CommandQuickPickItem) {

View File

@@ -21,7 +21,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
const gitUri = await GitUri.fromUri(uri, this.git); const gitUri = await GitUri.fromUri(uri, this.git);
let repoPath = gitUri.repoPath; let repoPath = gitUri.repoPath;
let workingFileName = path.relative(repoPath, gitUri.fsPath); let workingFileName = path.relative(repoPath || '', gitUri.fsPath);
if (!sha) { if (!sha) {
if (!editor) return undefined; if (!editor) return undefined;
@@ -48,21 +48,23 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
try { try {
if (!commit || (commit.type !== 'branch' && commit.type !== 'stash')) { if (!commit || (commit.type !== 'branch' && commit.type !== 'stash')) {
if (repoLog) { if (repoLog) {
commit = repoLog.commits.get(sha); commit = repoLog.commits.get(sha!);
// If we can't find the commit, kill the repoLog // If we can't find the commit, kill the repoLog
if (!commit) { if (commit === undefined) {
repoLog = undefined; repoLog = undefined;
} }
} }
if (!repoLog) { if (repoLog === undefined) {
const log = await this.git.getLogForRepo(repoPath, sha, 2); const log = await this.git.getLogForRepo(repoPath!, sha, 2);
if (!log) return window.showWarningMessage(`Unable to show commit details`); if (log === undefined) return window.showWarningMessage(`Unable to show commit details`);
commit = log.commits.get(sha); commit = log.commits.get(sha!);
} }
} }
if (commit === undefined) return window.showWarningMessage(`Unable to show commit details`);
if (!commit.workingFileName) { if (!commit.workingFileName) {
commit.workingFileName = workingFileName; commit.workingFileName = workingFileName;
} }

View File

@@ -46,24 +46,26 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
try { try {
if (!commit || (commit.type !== 'file' && commit.type !== 'stash')) { if (!commit || (commit.type !== 'file' && commit.type !== 'stash')) {
if (fileLog) { if (fileLog) {
commit = fileLog.commits.get(sha); commit = fileLog.commits.get(sha!);
// If we can't find the commit, kill the fileLog // If we can't find the commit, kill the fileLog
if (!commit) { if (commit === undefined) {
fileLog = undefined; fileLog = undefined;
} }
} }
if (!fileLog) { if (fileLog === undefined) {
commit = await this.git.getLogCommit(commit ? commit.repoPath : gitUri.repoPath, gitUri.fsPath, sha, { previous: true }); commit = await this.git.getLogCommit(commit ? commit.repoPath : gitUri.repoPath, gitUri.fsPath, sha, { previous: true });
if (!commit) return window.showWarningMessage(`Unable to show commit file details`); if (commit === undefined) return window.showWarningMessage(`Unable to show commit file details`);
} }
} }
if (commit === undefined) return window.showWarningMessage(`Unable to show commit file details`);
// Attempt to the most recent commit -- so that we can find the real working filename if there was a rename // Attempt to the most recent commit -- so that we can find the real working filename if there was a rename
commit.workingFileName = workingFileName; commit.workingFileName = workingFileName;
commit.workingFileName = await this.git.findWorkingFileName(commit); commit.workingFileName = await this.git.findWorkingFileName(commit);
const shortSha = sha.substring(0, 8); const shortSha = sha!.substring(0, 8);
if (!goBackCommand) { if (!goBackCommand) {
// Create a command to get back to the commit details // Create a command to get back to the commit details

View File

@@ -18,9 +18,10 @@ export class ShowQuickStashListCommand extends ActiveEditorCachedCommand {
try { try {
const repoPath = await this.git.getRepoPathFromUri(uri); const repoPath = await this.git.getRepoPathFromUri(uri);
if (!repoPath) return window.showWarningMessage(`Unable to show stashed changes`); if (repoPath === undefined) return window.showWarningMessage(`Unable to show stashed changes`);
const stash = await this.git.getStashList(repoPath); const stash = await this.git.getStashList(repoPath);
if (stash === undefined) return window.showWarningMessage(`Unable to show stashed changes`);
// Create a command to get back to here // Create a command to get back to here
const currentCommand = new CommandQuickPickItem({ const currentCommand = new CommandQuickPickItem({

View File

@@ -7,9 +7,9 @@ abstract class Comparer<T> {
class UriComparer extends Comparer<Uri> { class UriComparer extends Comparer<Uri> {
equals(lhs: Uri, rhs: Uri) { equals(lhs: Uri | undefined, rhs: Uri | undefined) {
if (!lhs && !rhs) return true; if (lhs === undefined && rhs === undefined) return true;
if ((lhs && !rhs) || (!lhs && rhs)) return false; if (lhs === undefined || rhs === undefined) return false;
return lhs.scheme === rhs.scheme && lhs.fsPath === rhs.fsPath; return lhs.scheme === rhs.scheme && lhs.fsPath === rhs.fsPath;
} }
@@ -17,9 +17,9 @@ class UriComparer extends Comparer<Uri> {
class TextDocumentComparer extends Comparer<TextDocument> { class TextDocumentComparer extends Comparer<TextDocument> {
equals(lhs: TextDocument, rhs: TextDocument) { equals(lhs: TextDocument | undefined, rhs: TextDocument | undefined) {
if (!lhs && !rhs) return true; if (lhs === undefined && rhs === undefined) return true;
if ((lhs && !rhs) || (!lhs && rhs)) return false; if (lhs === undefined || rhs === undefined) return false;
return uriComparer.equals(lhs.uri, rhs.uri); return uriComparer.equals(lhs.uri, rhs.uri);
} }
@@ -27,9 +27,9 @@ class TextDocumentComparer extends Comparer<TextDocument> {
class TextEditorComparer extends Comparer<TextEditor> { class TextEditorComparer extends Comparer<TextEditor> {
equals(lhs: TextEditor, rhs: TextEditor, options: { useId: boolean, usePosition: boolean } = { useId: false, usePosition: false }) { equals(lhs: TextEditor | undefined, rhs: TextEditor | undefined, options: { useId: boolean, usePosition: boolean } = { useId: false, usePosition: false }) {
if (!lhs && !rhs) return true; if (lhs === undefined && rhs === undefined) return true;
if ((lhs && !rhs) || (!lhs && rhs)) return false; if (lhs === undefined || rhs === undefined) return false;
if (options.usePosition && (lhs.viewColumn !== rhs.viewColumn)) return false; if (options.usePosition && (lhs.viewColumn !== rhs.viewColumn)) return false;

View File

@@ -58,7 +58,7 @@ export interface ICodeLensConfig {
} }
export interface ICodeLensLanguageLocation { export interface ICodeLensLanguageLocation {
language: string; language: string | undefined;
location: CodeLensLocation; location: CodeLensLocation;
customSymbols?: string[]; customSymbols?: string[];
} }

View File

@@ -30,14 +30,14 @@ export async function activate(context: ExtensionContext) {
Logger.configure(context); Logger.configure(context);
Telemetry.configure(ApplicationInsightsKey); Telemetry.configure(ApplicationInsightsKey);
const gitlens = extensions.getExtension(QualifiedExtensionId); const gitlens = extensions.getExtension(QualifiedExtensionId)!;
const gitlensVersion = gitlens.packageJSON.version; const gitlensVersion = gitlens.packageJSON.version;
const rootPath = workspace.rootPath && workspace.rootPath.replace(/\\/g, '/'); const rootPath = workspace.rootPath && workspace.rootPath.replace(/\\/g, '/');
Logger.log(`GitLens(v${gitlensVersion}) active: ${rootPath}`); Logger.log(`GitLens(v${gitlensVersion}) active: ${rootPath}`);
const config = workspace.getConfiguration().get<IConfig>(ExtensionKey); const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
const gitPath = config.advanced.git; const gitPath = cfg.advanced.git;
try { try {
await GitService.getGitPath(gitPath); await GitService.getGitPath(gitPath);
@@ -114,7 +114,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new StashSaveCommand(git)); context.subscriptions.push(new StashSaveCommand(git));
context.subscriptions.push(new ToggleCodeLensCommand(git)); context.subscriptions.push(new ToggleCodeLensCommand(git));
Telemetry.trackEvent('initialized', Objects.flatten(config, 'config', true)); Telemetry.trackEvent('initialized', Objects.flatten(cfg, 'config', true));
} }
// this method is called when your extension is deactivated // this method is called when your extension is deactivated

View File

@@ -54,8 +54,8 @@ export class Git {
return git; return git;
} }
static async getRepoPath(cwd: string) { static async getRepoPath(cwd: string | undefined) {
if (!cwd) return ''; if (cwd === undefined) return '';
const data = await gitCommand(cwd, 'rev-parse', '--show-toplevel'); const data = await gitCommand(cwd, 'rev-parse', '--show-toplevel');
if (!data) return ''; if (!data) return '';
@@ -63,7 +63,7 @@ export class Git {
return data.replace(/\r?\n|\r/g, '').replace(/\\/g, '/'); return data.replace(/\r?\n|\r/g, '').replace(/\\/g, '/');
} }
static async getVersionedFile(repoPath: string, fileName: string, branchOrSha: string) { static async getVersionedFile(repoPath: string | undefined, fileName: string, branchOrSha: string) {
const data = await Git.show(repoPath, fileName, branchOrSha); const data = await Git.show(repoPath, fileName, branchOrSha);
const suffix = Git.isSha(branchOrSha) ? branchOrSha.substring(0, 8) : branchOrSha; const suffix = Git.isSha(branchOrSha) ? branchOrSha.substring(0, 8) : branchOrSha;
@@ -112,7 +112,7 @@ export class Git {
} }
} }
else { else {
repoPath = this.normalizePath(extract ? path.dirname(fileName) : repoPath); repoPath = this.normalizePath(extract ? path.dirname(fileName) : repoPath!);
fileName = this.normalizePath(extract ? path.basename(fileName) : fileName); fileName = this.normalizePath(extract ? path.basename(fileName) : fileName);
} }
@@ -126,7 +126,7 @@ export class Git {
// Git commands // Git commands
static blame(repoPath: string, fileName: string, sha?: string, startLine?: number, endLine?: number) { static blame(repoPath: string | undefined, fileName: string, sha?: string, startLine?: number, endLine?: number) {
const [file, root] = Git.splitPath(fileName, repoPath); const [file, root] = Git.splitPath(fileName, repoPath);
const params = [`blame`, `--root`, `--incremental`]; const params = [`blame`, `--root`, `--incremental`];
@@ -208,7 +208,7 @@ export class Git {
} }
// If we are looking for a specific sha don't exclude merge commits // If we are looking for a specific sha don't exclude merge commits
if (!sha || maxCount > 2) { if (!sha || maxCount! > 2) {
params.push(`--no-merges`); params.push(`--no-merges`);
} }
else { else {
@@ -236,7 +236,7 @@ export class Git {
return gitCommand(root, ...params); return gitCommand(root, ...params);
} }
static log_search(repoPath: string, search?: string[], maxCount?: number) { static log_search(repoPath: string, search: string[] = [], maxCount?: number) {
const params = [...defaultLogParams, `-m`, `-i`]; const params = [...defaultLogParams, `-m`, `-i`];
if (maxCount) { if (maxCount) {
params.push(`-n${maxCount}`); params.push(`-n${maxCount}`);
@@ -262,7 +262,7 @@ export class Git {
return gitCommand(repoPath, 'remote', 'get-url', remote); return gitCommand(repoPath, 'remote', 'get-url', remote);
} }
static show(repoPath: string, fileName: string, branchOrSha: string) { static show(repoPath: string | undefined, fileName: string, branchOrSha: string) {
const [file, root] = Git.splitPath(fileName, repoPath); const [file, root] = Git.splitPath(fileName, repoPath);
branchOrSha = branchOrSha.replace('^', ''); branchOrSha = branchOrSha.replace('^', '');

View File

@@ -7,7 +7,7 @@ import { Logger } from '../logger';
export interface BlameabilityChangeEvent { export interface BlameabilityChangeEvent {
blameable: boolean; blameable: boolean;
editor: TextEditor; editor: TextEditor | undefined;
} }
export class GitContextTracker extends Disposable { export class GitContextTracker extends Disposable {
@@ -18,8 +18,8 @@ export class GitContextTracker extends Disposable {
} }
private _disposable: Disposable; private _disposable: Disposable;
private _documentChangeDisposable: Disposable; private _documentChangeDisposable: Disposable | undefined;
private _editor: TextEditor; private _editor: TextEditor | undefined;
private _gitEnabled: boolean; private _gitEnabled: boolean;
private _isBlameable: boolean; private _isBlameable: boolean;
@@ -47,7 +47,7 @@ export class GitContextTracker extends Disposable {
} }
_onConfigurationChanged() { _onConfigurationChanged() {
const gitEnabled = workspace.getConfiguration('git').get<boolean>('enabled'); const gitEnabled = workspace.getConfiguration('git').get<boolean>('enabled', true);
if (this._gitEnabled !== gitEnabled) { if (this._gitEnabled !== gitEnabled) {
this._gitEnabled = gitEnabled; this._gitEnabled = gitEnabled;
setCommandContext(CommandContext.Enabled, gitEnabled); setCommandContext(CommandContext.Enabled, gitEnabled);
@@ -55,9 +55,9 @@ export class GitContextTracker extends Disposable {
} }
} }
private _onActiveTextEditorChanged(editor: TextEditor) { private _onActiveTextEditorChanged(editor: TextEditor | undefined) {
this._editor = editor; this._editor = editor;
this._updateContext(this._gitEnabled && editor); this._updateContext(this._gitEnabled ? editor : undefined);
this._subscribeToDocumentChanges(); this._subscribeToDocumentChanges();
} }
@@ -97,9 +97,9 @@ export class GitContextTracker extends Disposable {
this._documentChangeDisposable = undefined; this._documentChangeDisposable = undefined;
} }
private async _updateContext(editor: TextEditor) { private async _updateContext(editor: TextEditor | undefined) {
try { try {
const gitUri = editor && await GitUri.fromUri(editor.document.uri, this.git); const gitUri = editor === undefined ? undefined : await GitUri.fromUri(editor.document.uri, this.git);
await Promise.all([ await Promise.all([
this._updateEditorContext(gitUri, editor), this._updateEditorContext(gitUri, editor),
@@ -131,12 +131,12 @@ export class GitContextTracker extends Disposable {
private async _updateEditorContext(uri: GitUri | undefined, editor: TextEditor | undefined) { private async _updateEditorContext(uri: GitUri | undefined, editor: TextEditor | undefined) {
try { try {
const tracked = uri && await this.git.isTracked(uri); const tracked = uri === undefined ? false : await this.git.isTracked(uri);
setCommandContext(CommandContext.IsTracked, tracked); setCommandContext(CommandContext.IsTracked, tracked);
let blameable = tracked && editor && editor.document && !editor.document.isDirty; let blameable = tracked && (editor !== undefined && editor.document !== undefined && !editor.document.isDirty);
if (blameable) { if (blameable) {
blameable = await this.git.getBlameability(uri); blameable = uri === undefined ? false : await this.git.getBlameability(uri);
} }
this._updateBlameability(blameable, true); this._updateBlameability(blameable, true);

View File

@@ -43,7 +43,7 @@ export class GitUri extends Uri {
const commit = commitOrRepoPath; const commit = commitOrRepoPath;
base._fsPath = path.resolve(commit.repoPath, commit.originalFileName || commit.fileName); base._fsPath = path.resolve(commit.repoPath, commit.originalFileName || commit.fileName);
if (!GitService.isUncommitted(commit.sha)) { if (commit.sha !== undefined && !GitService.isUncommitted(commit.sha)) {
this.sha = commit.sha; this.sha = commit.sha;
this.repoPath = commit.repoPath; this.repoPath = commit.repoPath;
} }
@@ -72,7 +72,7 @@ export class GitUri extends Uri {
} }
getRelativePath(): string { getRelativePath(): string {
return GitService.normalizePath(path.relative(this.repoPath, this.fsPath)); return GitService.normalizePath(path.relative(this.repoPath || '', this.fsPath));
} }
static async fromUri(uri: Uri, git: GitService) { static async fromUri(uri: Uri, git: GitService) {

View File

@@ -13,7 +13,7 @@ export interface IGitCommit {
repoPath: string; repoPath: string;
sha: string; sha: string;
fileName: string; fileName: string;
author: string; author?: string;
date: Date; date: Date;
message: string; message: string;
lines: IGitCommitLine[]; lines: IGitCommitLine[];

View File

@@ -40,7 +40,7 @@ export class GitLogCommit extends GitCommit {
this.status = fileStatus.status; this.status = fileStatus.status;
} }
else { else {
this.fileStatuses = [{ status: status, fileName: fileName, originalFileName: originalFileName }]; this.fileStatuses = [{ status: status, fileName: fileName, originalFileName: originalFileName } as IGitStatusFile];
this.status = status; this.status = status;
} }
} }

View File

@@ -19,7 +19,7 @@ export class GitStashCommit extends GitLogCommit {
previousSha?: string, previousSha?: string,
previousFileName?: string previousFileName?: string
) { ) {
super('stash', repoPath, sha, fileName, undefined, date, message, status, fileStatuses, lines, originalFileName, previousSha, previousFileName); super('stash', repoPath, sha, fileName, 'You', date, message, status, fileStatuses, lines, originalFileName, previousSha, previousFileName);
} }
get shortSha() { get shortSha() {

View File

@@ -10,7 +10,7 @@ interface IBlameEntry {
originalLine: number; originalLine: number;
lineCount: number; lineCount: number;
author?: string; author: string;
// authorEmail?: string; // authorEmail?: string;
authorDate?: string; authorDate?: string;
authorTimeZone?: string; authorTimeZone?: string;
@@ -30,7 +30,7 @@ interface IBlameEntry {
export class GitBlameParser { export class GitBlameParser {
private static _parseEntries(data: string): IBlameEntry[] { private static _parseEntries(data: string): IBlameEntry[] | undefined {
if (!data) return undefined; if (!data) return undefined;
const lines = data.split('\n'); const lines = data.split('\n');
@@ -38,7 +38,7 @@ export class GitBlameParser {
const entries: IBlameEntry[] = []; const entries: IBlameEntry[] = [];
let entry: IBlameEntry; let entry: IBlameEntry | undefined = undefined;
let position = -1; let position = -1;
while (++position < lines.length) { while (++position < lines.length) {
let lineParts = lines[position].split(' '); let lineParts = lines[position].split(' ');
@@ -46,13 +46,13 @@ export class GitBlameParser {
continue; continue;
} }
if (!entry) { if (entry === undefined) {
entry = { entry = {
sha: lineParts[0], sha: lineParts[0],
originalLine: parseInt(lineParts[1], 10) - 1, originalLine: parseInt(lineParts[1], 10) - 1,
line: parseInt(lineParts[2], 10) - 1, line: parseInt(lineParts[2], 10) - 1,
lineCount: parseInt(lineParts[3], 10) lineCount: parseInt(lineParts[3], 10)
}; } as IBlameEntry;
continue; continue;
} }
@@ -116,7 +116,7 @@ export class GitBlameParser {
return entries; return entries;
} }
static parse(data: string, repoPath: string, fileName: string): IGitBlame { static parse(data: string, repoPath: string | undefined, fileName: string): IGitBlame | undefined {
const entries = this._parseEntries(data); const entries = this._parseEntries(data);
if (!entries) return undefined; if (!entries) return undefined;
@@ -129,24 +129,26 @@ export class GitBlameParser {
for (let i = 0, len = entries.length; i < len; i++) { for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i]; const entry = entries[i];
if (i === 0 && !repoPath) { if (i === 0 && repoPath === undefined) {
// Try to get the repoPath from the most recent commit // Try to get the repoPath from the most recent commit
repoPath = Git.normalizePath(fileName.replace(fileName.startsWith('/') ? `/${entry.fileName}` : entry.fileName, '')); repoPath = Git.normalizePath(fileName.replace(fileName.startsWith('/') ? `/${entry.fileName}` : entry.fileName!, ''));
relativeFileName = Git.normalizePath(path.relative(repoPath, fileName)); relativeFileName = Git.normalizePath(path.relative(repoPath, fileName));
} }
let commit = commits.get(entry.sha); let commit = commits.get(entry.sha);
if (!commit) { if (commit === undefined) {
let author = authors.get(entry.author); if (entry.author !== undefined) {
if (!author) { let author = authors.get(entry.author);
author = { if (author === undefined) {
name: entry.author, author = {
lineCount: 0 name: entry.author,
}; lineCount: 0
authors.set(entry.author, author); };
authors.set(entry.author, author);
}
} }
commit = new GitCommit('blame', repoPath, entry.sha, relativeFileName, entry.author, moment(`${entry.authorDate} ${entry.authorTimeZone}`, 'X +-HHmm').toDate(), entry.summary); commit = new GitCommit('blame', repoPath!, entry.sha, relativeFileName!, entry.author, moment(`${entry.authorDate} ${entry.authorTimeZone}`, 'X +-HHmm').toDate(), entry.summary!);
if (relativeFileName !== entry.fileName) { if (relativeFileName !== entry.fileName) {
commit.originalFileName = entry.fileName; commit.originalFileName = entry.fileName;
@@ -176,7 +178,14 @@ export class GitBlameParser {
} }
} }
commits.forEach(c => authors.get(c.author).lineCount += c.lines.length); commits.forEach(c => {
if (c.author === undefined) return;
const author = authors.get(c.author);
if (author === undefined) return;
author.lineCount += c.lines.length;
});
const sortedAuthors: Map<string, IGitAuthor> = new Map(); const sortedAuthors: Map<string, IGitAuthor> = new Map();
// const values = // const values =

View File

@@ -8,7 +8,7 @@ import * as path from 'path';
interface ILogEntry { interface ILogEntry {
sha: string; sha: string;
author?: string; author: string;
authorDate?: string; authorDate?: string;
// committer?: string; // committer?: string;
@@ -29,7 +29,7 @@ const diffRegex = /diff --git a\/(.*) b\/(.*)/;
export class GitLogParser { export class GitLogParser {
private static _parseEntries(data: string, type: GitCommitType, maxCount: number | undefined, reverse: boolean): ILogEntry[] { private static _parseEntries(data: string, type: GitCommitType, maxCount: number | undefined, reverse: boolean): ILogEntry[] | undefined {
if (!data) return undefined; if (!data) return undefined;
const lines = data.split('\n'); const lines = data.split('\n');
@@ -37,7 +37,7 @@ export class GitLogParser {
const entries: ILogEntry[] = []; const entries: ILogEntry[] = [];
let entry: ILogEntry; let entry: ILogEntry | undefined = undefined;
let position = -1; let position = -1;
while (++position < lines.length) { while (++position < lines.length) {
// Since log --reverse doesn't properly honor a max count -- enforce it here // Since log --reverse doesn't properly honor a max count -- enforce it here
@@ -48,12 +48,12 @@ export class GitLogParser {
continue; continue;
} }
if (!entry) { if (entry === undefined) {
if (!Git.shaRegex.test(lineParts[0])) continue; if (!Git.shaRegex.test(lineParts[0])) continue;
entry = { entry = {
sha: lineParts[0] sha: lineParts[0]
}; } as ILogEntry;
continue; continue;
} }
@@ -118,10 +118,12 @@ export class GitLogParser {
if (lineParts[0] === 'diff') { if (lineParts[0] === 'diff') {
diff = true; diff = true;
const matches = diffRegex.exec(line); const matches = diffRegex.exec(line);
entry.fileName = matches[1]; if (matches != null) {
const originalFileName = matches[2]; entry.fileName = matches[1];
if (entry.fileName !== originalFileName) { const originalFileName = matches[2];
entry.originalFileName = originalFileName; if (entry.fileName !== originalFileName) {
entry.originalFileName = originalFileName;
}
} }
continue; continue;
} }
@@ -133,7 +135,7 @@ export class GitLogParser {
const status = { const status = {
status: line[0] as GitStatusFileStatus, status: line[0] as GitStatusFileStatus,
fileName: line.substring(1), fileName: line.substring(1),
originalFileName: undefined as string originalFileName: undefined
} as IGitStatusFile; } as IGitStatusFile;
this._parseFileName(status); this._parseFileName(status);
@@ -164,7 +166,7 @@ export class GitLogParser {
return entries; return entries;
} }
static parse(data: string, type: GitCommitType, repoPath: string | undefined, fileName: string | undefined, sha: string | undefined, maxCount: number | undefined, reverse: boolean, range: Range): IGitLog { static parse(data: string, type: GitCommitType, repoPath: string | undefined, fileName: string | undefined, sha: string | undefined, maxCount: number | undefined, reverse: boolean, range: Range | undefined): IGitLog | undefined {
const entries = this._parseEntries(data, type, maxCount, reverse); const entries = this._parseEntries(data, type, maxCount, reverse);
if (!entries) return undefined; if (!entries) return undefined;
@@ -172,7 +174,7 @@ export class GitLogParser {
const commits: Map<string, GitLogCommit> = new Map(); const commits: Map<string, GitLogCommit> = new Map();
let relativeFileName: string; let relativeFileName: string;
let recentCommit: GitLogCommit; let recentCommit: GitLogCommit | undefined = undefined;
if (repoPath !== undefined) { if (repoPath !== undefined) {
repoPath = Git.normalizePath(repoPath); repoPath = Git.normalizePath(repoPath);
@@ -184,28 +186,30 @@ export class GitLogParser {
const entry = entries[i]; const entry = entries[i];
if (i === 0 && type === 'file' && !repoPath) { if (i === 0 && repoPath === undefined && type === 'file' && fileName !== undefined) {
// Try to get the repoPath from the most recent commit // Try to get the repoPath from the most recent commit
repoPath = Git.normalizePath(fileName.replace(fileName.startsWith('/') ? `/${entry.fileName}` : entry.fileName, '')); repoPath = Git.normalizePath(fileName.replace(fileName.startsWith('/') ? `/${entry.fileName}` : entry.fileName!, ''));
relativeFileName = Git.normalizePath(path.relative(repoPath, fileName)); relativeFileName = Git.normalizePath(path.relative(repoPath, fileName));
} }
else { else {
relativeFileName = entry.fileName; relativeFileName = entry.fileName!;
} }
let commit = commits.get(entry.sha); let commit = commits.get(entry.sha);
if (!commit) { if (commit === undefined) {
let author = authors.get(entry.author); if (entry.author !== undefined) {
if (!author) { let author = authors.get(entry.author);
author = { if (author === undefined) {
name: entry.author, author = {
lineCount: 0 name: entry.author,
}; lineCount: 0
authors.set(entry.author, author); };
authors.set(entry.author, author);
}
} }
commit = new GitLogCommit(type, repoPath, entry.sha, relativeFileName, entry.author, moment(entry.authorDate).toDate(), entry.summary, entry.status, entry.fileStatuses, undefined, entry.originalFileName); commit = new GitLogCommit(type, repoPath!, entry.sha, relativeFileName, entry.author, moment(entry.authorDate).toDate(), entry.summary!, entry.status, entry.fileStatuses, undefined, entry.originalFileName);
commit.parentShas = entry.parentShas; commit.parentShas = entry.parentShas!;
if (relativeFileName !== entry.fileName) { if (relativeFileName !== entry.fileName) {
commit.originalFileName = entry.fileName; commit.originalFileName = entry.fileName;
@@ -217,7 +221,7 @@ export class GitLogParser {
// Logger.log(`merge commit? ${entry.sha}`); // Logger.log(`merge commit? ${entry.sha}`);
// } // }
if (recentCommit) { if (recentCommit !== undefined) {
recentCommit.previousSha = commit.sha; recentCommit.previousSha = commit.sha;
// If the commit sha's match (merge commit), just forward it along // If the commit sha's match (merge commit), just forward it along
@@ -232,7 +236,14 @@ export class GitLogParser {
recentCommit = commit; recentCommit = commit;
} }
commits.forEach(c => authors.get(c.author).lineCount += c.lines.length); commits.forEach(c => {
if (c.author === undefined) return;
const author = authors.get(c.author);
if (author === undefined) return;
author.lineCount += c.lines.length;
});
const sortedAuthors: Map<string, IGitAuthor> = new Map(); const sortedAuthors: Map<string, IGitAuthor> = new Map();
// const values = // const values =
@@ -258,10 +269,12 @@ export class GitLogParser {
} }
private static _parseFileName(entry: { fileName?: string, originalFileName?: string }) { private static _parseFileName(entry: { fileName?: string, originalFileName?: string }) {
if (entry.fileName === undefined) return;
const index = entry.fileName.indexOf('\t') + 1; const index = entry.fileName.indexOf('\t') + 1;
if (index) { if (index > 0) {
const next = entry.fileName.indexOf('\t', index) + 1; const next = entry.fileName.indexOf('\t', index) + 1;
if (next) { if (next > 0) {
entry.originalFileName = entry.fileName.substring(index, next - 1); entry.originalFileName = entry.fileName.substring(index, next - 1);
entry.fileName = entry.fileName.substring(next); entry.fileName = entry.fileName.substring(next);
} }

View File

@@ -6,15 +6,15 @@ import * as moment from 'moment';
interface IStashEntry { interface IStashEntry {
sha: string; sha: string;
date?: string; date?: string;
fileNames?: string; fileNames: string;
fileStatuses?: IGitStatusFile[]; fileStatuses?: IGitStatusFile[];
summary?: string; summary: string;
stashName?: string; stashName: string;
} }
export class GitStashParser { export class GitStashParser {
private static _parseEntries(data: string): IStashEntry[] { private static _parseEntries(data: string): IStashEntry[] | undefined {
if (!data) return undefined; if (!data) return undefined;
const lines = data.split('\n'); const lines = data.split('\n');
@@ -22,7 +22,7 @@ export class GitStashParser {
const entries: IStashEntry[] = []; const entries: IStashEntry[] = [];
let entry: IStashEntry; let entry: IStashEntry | undefined = undefined;
let position = -1; let position = -1;
while (++position < lines.length) { while (++position < lines.length) {
let lineParts = lines[position].split(' '); let lineParts = lines[position].split(' ');
@@ -30,12 +30,12 @@ export class GitStashParser {
continue; continue;
} }
if (!entry) { if (entry === undefined) {
if (!Git.shaRegex.test(lineParts[0])) continue; if (!Git.shaRegex.test(lineParts[0])) continue;
entry = { entry = {
sha: lineParts[0] sha: lineParts[0]
}; } as IStashEntry;
continue; continue;
} }
@@ -86,7 +86,7 @@ export class GitStashParser {
const status = { const status = {
status: line[0] as GitStatusFileStatus, status: line[0] as GitStatusFileStatus,
fileName: line.substring(1), fileName: line.substring(1),
originalFileName: undefined as string originalFileName: undefined
} as IGitStatusFile; } as IGitStatusFile;
this._parseFileName(status); this._parseFileName(status);
@@ -109,9 +109,9 @@ export class GitStashParser {
return entries; return entries;
} }
static parse(data: string, repoPath: string): IGitStash { static parse(data: string, repoPath: string): IGitStash | undefined {
const entries = this._parseEntries(data); const entries = this._parseEntries(data);
if (!entries) return undefined; if (entries === undefined) return undefined;
const commits: Map<string, GitStashCommit> = new Map(); const commits: Map<string, GitStashCommit> = new Map();
@@ -119,8 +119,8 @@ export class GitStashParser {
const entry = entries[i]; const entry = entries[i];
let commit = commits.get(entry.sha); let commit = commits.get(entry.sha);
if (!commit) { if (commit !== undefined) {
commit = new GitStashCommit(entry.stashName, repoPath, entry.sha, entry.fileNames, moment(entry.date).toDate(), entry.summary, undefined, entry.fileStatuses); commit = new GitStashCommit(entry.stashName, repoPath, entry.sha, entry.fileNames, moment(entry.date).toDate(), entry.summary, undefined, entry.fileStatuses) as GitStashCommit;
commits.set(entry.sha, commit); commits.set(entry.sha, commit);
} }
} }
@@ -132,10 +132,12 @@ export class GitStashParser {
} }
private static _parseFileName(entry: { fileName?: string, originalFileName?: string }) { private static _parseFileName(entry: { fileName?: string, originalFileName?: string }) {
if (entry.fileName === undefined) return;
const index = entry.fileName.indexOf('\t') + 1; const index = entry.fileName.indexOf('\t') + 1;
if (index) { if (index > 0) {
const next = entry.fileName.indexOf('\t', index) + 1; const next = entry.fileName.indexOf('\t', index) + 1;
if (next) { if (next > 0) {
entry.originalFileName = entry.fileName.substring(index, next - 1); entry.originalFileName = entry.fileName.substring(index, next - 1);
entry.fileName = entry.fileName.substring(next); entry.fileName = entry.fileName.substring(next);
} }

View File

@@ -13,20 +13,22 @@ const behindStatusV1Regex = /(?:behind ([0-9]+))/;
export class GitStatusParser { export class GitStatusParser {
static parse(data: string, repoPath: string, porcelainVersion: number): IGitStatus { static parse(data: string, repoPath: string, porcelainVersion: number): IGitStatus | undefined {
if (!data) return undefined; if (!data) return undefined;
const lines = data.split('\n').filter(_ => !!_); const lines = data.split('\n').filter(_ => !!_);
if (!lines.length) return undefined; if (!lines.length) return undefined;
const status = { const status = {
branch: '',
repoPath: Git.normalizePath(repoPath), repoPath: Git.normalizePath(repoPath),
sha: '',
state: { state: {
ahead: 0, ahead: 0,
behind: 0 behind: 0
}, },
files: [] files: []
} as IGitStatus; };
if (porcelainVersion >= 2) { if (porcelainVersion >= 2) {
this._parseV2(lines, repoPath, status); this._parseV2(lines, repoPath, status);
@@ -50,10 +52,10 @@ export class GitStatusParser {
const upstreamStatus = lineParts.slice(2).join(' '); const upstreamStatus = lineParts.slice(2).join(' ');
const aheadStatus = aheadStatusV1Regex.exec(upstreamStatus); const aheadStatus = aheadStatusV1Regex.exec(upstreamStatus);
status.state.ahead = +aheadStatus[1] || 0; status.state.ahead = aheadStatus == null ? 0 : +aheadStatus[1] || 0;
const behindStatus = behindStatusV1Regex.exec(upstreamStatus); const behindStatus = behindStatusV1Regex.exec(upstreamStatus);
status.state.behind = +behindStatus[1] || 0; status.state.behind = behindStatus == null ? 0 : +behindStatus[1] || 0;
} }
} }
else { else {
@@ -97,7 +99,7 @@ export class GitStatusParser {
} }
else { else {
let lineParts = line.split(' '); let lineParts = line.split(' ');
let entry: IFileStatusEntry; let entry: IFileStatusEntry | undefined = undefined;
switch (lineParts[0][0]) { switch (lineParts[0][0]) {
case '1': // normal case '1': // normal
entry = this._parseFileEntry(lineParts[1], lineParts.slice(8).join(' ')); entry = this._parseFileEntry(lineParts[1], lineParts.slice(8).join(' '));
@@ -114,7 +116,7 @@ export class GitStatusParser {
break; break;
} }
if (entry) { if (entry !== undefined) {
status.files.push(new GitStatusFile(repoPath, entry.status, entry.fileName, entry.staged, entry.originalFileName)); status.files.push(new GitStatusFile(repoPath, entry.status, entry.fileName, entry.staged, entry.originalFileName));
} }
} }
@@ -130,6 +132,6 @@ export class GitStatusParser {
fileName: fileName, fileName: fileName,
originalFileName: originalFileName, originalFileName: originalFileName,
staged: !!indexStatus staged: !!indexStatus
}; } as IFileStatusEntry;
} }
} }

View File

@@ -19,9 +19,11 @@ const UrlRegex = /^(?:git:\/\/(.*?)\/|https:\/\/(.*?)\/|http:\/\/(.*?)\/|git@(.*
export class RemoteProviderFactory { export class RemoteProviderFactory {
static getRemoteProvider(url: string): RemoteProvider { static getRemoteProvider(url: string): RemoteProvider | undefined {
try { try {
const match = UrlRegex.exec(url); const match = UrlRegex.exec(url);
if (match == null) return undefined;
const domain = match[1] || match[2] || match[3] || match[4] || match[5]; const domain = match[1] || match[2] || match[3] || match[4] || match[5];
const path = match[6].replace(/\.git/, ''); const path = match[6].replace(/\.git/, '');

View File

@@ -26,10 +26,12 @@ export abstract class RemoteProvider {
protected abstract getUrlForBranch(branch: string): string; protected abstract getUrlForBranch(branch: string): string;
protected abstract getUrlForCommit(sha: string): string; protected abstract getUrlForCommit(sha: string): string;
protected abstract getUrlForFile(fileName: string, branch: string, sha?: string, range?: Range): string; protected abstract getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string;
private async _openUrl(url: string): Promise<{}> { private async _openUrl(url: string): Promise<{} | undefined> {
return url && commands.executeCommand(BuiltInCommands.Open, Uri.parse(url)); if (url === undefined) return undefined;
return commands.executeCommand(BuiltInCommands.Open, Uri.parse(url));
} }
open(type: 'branch', branch: string): Promise<{}>; open(type: 'branch', branch: string): Promise<{}>;

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
import { Functions, Iterables, Strings } from './system'; import { Functions, Iterables, Strings } from './system';
import { CancellationToken, CodeLens, CodeLensProvider, commands, DocumentSelector, Event, EventEmitter, ExtensionContext, Position, Range, SymbolInformation, SymbolKind, TextDocument, Uri, workspace } from 'vscode'; import { CancellationToken, CodeLens, CodeLensProvider, Command, commands, DocumentSelector, Event, EventEmitter, ExtensionContext, Position, Range, SymbolInformation, SymbolKind, TextDocument, Uri, workspace } from 'vscode';
import { Commands } from './commands'; import { Commands } from './commands';
import { BuiltInCommands, DocumentSchemes, ExtensionKey } from './constants'; import { BuiltInCommands, DocumentSchemes, ExtensionKey } from './constants';
import { CodeLensCommand, CodeLensLocation, IConfig, ICodeLensLanguageLocation } from './configuration'; import { CodeLensCommand, CodeLensLocation, IConfig, ICodeLensLanguageLocation } from './configuration';
@@ -10,22 +10,22 @@ import * as moment from 'moment';
export class GitRecentChangeCodeLens extends CodeLens { export class GitRecentChangeCodeLens extends CodeLens {
constructor(private blame: () => IGitBlameLines, public uri: GitUri, public symbolKind: SymbolKind, public blameRange: Range, public isFullRange: boolean, range: Range) { constructor(private blame: () => IGitBlameLines | undefined, public uri: GitUri, public symbolKind: SymbolKind, public blameRange: Range, public isFullRange: boolean, range: Range) {
super(range); super(range);
} }
getBlame(): IGitBlameLines { getBlame(): IGitBlameLines | undefined {
return this.blame(); return this.blame();
} }
} }
export class GitAuthorsCodeLens extends CodeLens { export class GitAuthorsCodeLens extends CodeLens {
constructor(private blame: () => IGitBlameLines, public uri: GitUri, public symbolKind: SymbolKind, public blameRange: Range, public isFullRange: boolean, range: Range) { constructor(private blame: () => IGitBlameLines | undefined, public uri: GitUri, public symbolKind: SymbolKind, public blameRange: Range, public isFullRange: boolean, range: Range) {
super(range); super(range);
} }
getBlame(): IGitBlameLines { getBlame(): IGitBlameLines | undefined {
return this.blame(); return this.blame();
} }
} }
@@ -43,11 +43,11 @@ export class GitCodeLensProvider implements CodeLensProvider {
private _documentIsDirty: boolean; private _documentIsDirty: boolean;
constructor(context: ExtensionContext, private git: GitService) { constructor(context: ExtensionContext, private git: GitService) {
this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey); this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
} }
reset() { reset() {
this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey); this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
Logger.log('Triggering a reset of the git CodeLens provider'); Logger.log('Triggering a reset of the git CodeLens provider');
this._onDidChangeCodeLenses.fire(); this._onDidChangeCodeLenses.fire();
@@ -56,7 +56,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> { async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
this._documentIsDirty = document.isDirty; this._documentIsDirty = document.isDirty;
let languageLocations = this._config.codeLens.languageLocations.find(_ => _.language.toLowerCase() === document.languageId); let languageLocations = this._config.codeLens.languageLocations.find(_ => _.language !== undefined && _.language.toLowerCase() === document.languageId);
if (languageLocations == null) { if (languageLocations == null) {
languageLocations = { languageLocations = {
language: undefined, language: undefined,
@@ -72,10 +72,10 @@ export class GitCodeLensProvider implements CodeLensProvider {
const gitUri = await GitUri.fromUri(document.uri, this.git); const gitUri = await GitUri.fromUri(document.uri, this.git);
const blamePromise = this.git.getBlameForFile(gitUri); const blamePromise = this.git.getBlameForFile(gitUri);
let blame: IGitBlame; let blame: IGitBlame | undefined;
if (languageLocations.location === CodeLensLocation.Document) { if (languageLocations.location === CodeLensLocation.Document) {
blame = await blamePromise; blame = await blamePromise;
if (!blame || !blame.lines.length) return lenses; if (blame === undefined || !blame.lines.length) return lenses;
} }
else { else {
const values = await Promise.all([ const values = await Promise.all([
@@ -84,25 +84,25 @@ export class GitCodeLensProvider implements CodeLensProvider {
]); ]);
blame = values[0] as IGitBlame; blame = values[0] as IGitBlame;
if (!blame || !blame.lines.length) return lenses; if (blame === undefined || !blame.lines.length) return lenses;
const symbols = values[1] as SymbolInformation[]; const symbols = values[1] as SymbolInformation[];
Logger.log('GitCodeLensProvider.provideCodeLenses:', `${symbols.length} symbol(s) found`); Logger.log('GitCodeLensProvider.provideCodeLenses:', `${symbols.length} symbol(s) found`);
symbols.forEach(sym => this._provideCodeLens(gitUri, document, sym, languageLocations, blame, lenses)); symbols.forEach(sym => this._provideCodeLens(gitUri, document, sym, languageLocations!, blame!, lenses));
} }
if (languageLocations.location !== CodeLensLocation.Custom || (languageLocations.customSymbols || []).find(_ => _.toLowerCase() === 'file')) { if (languageLocations.location !== CodeLensLocation.Custom || (languageLocations.customSymbols || []).find(_ => _.toLowerCase() === 'file')) {
// Check if we have a lens for the whole document -- if not add one // Check if we have a lens for the whole document -- if not add one
if (!lenses.find(l => l.range.start.line === 0 && l.range.end.line === 0)) { if (!lenses.find(l => l.range.start.line === 0 && l.range.end.line === 0)) {
const blameRange = document.validateRange(new Range(0, 1000000, 1000000, 1000000)); const blameRange = document.validateRange(new Range(0, 1000000, 1000000, 1000000));
let blameForRangeFn: () => IGitBlameLines; let blameForRangeFn: (() => IGitBlameLines | undefined) | undefined = undefined;
if (this._documentIsDirty || this._config.codeLens.recentChange.enabled) { if (this._documentIsDirty || this._config.codeLens.recentChange.enabled) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange)); blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame!, gitUri, blameRange));
lenses.push(new GitRecentChangeCodeLens(blameForRangeFn, gitUri, SymbolKind.File, blameRange, true, new Range(0, 0, 0, blameRange.start.character))); lenses.push(new GitRecentChangeCodeLens(blameForRangeFn, gitUri, SymbolKind.File, blameRange, true, new Range(0, 0, 0, blameRange.start.character)));
} }
if (this._config.codeLens.authors.enabled) { if (this._config.codeLens.authors.enabled) {
if (!blameForRangeFn) { if (blameForRangeFn === undefined) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange)); blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame!, gitUri, blameRange));
} }
if (!this._documentIsDirty) { if (!this._documentIsDirty) {
lenses.push(new GitAuthorsCodeLens(blameForRangeFn, gitUri, SymbolKind.File, blameRange, true, new Range(0, 1, 0, blameRange.start.character))); lenses.push(new GitAuthorsCodeLens(blameForRangeFn, gitUri, SymbolKind.File, blameRange, true, new Range(0, 1, 0, blameRange.start.character)));
@@ -191,7 +191,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
startChar += Math.floor(symbol.name.length / 2); startChar += Math.floor(symbol.name.length / 2);
} }
let blameForRangeFn: () => IGitBlameLines; let blameForRangeFn: (() => IGitBlameLines | undefined) | undefined = undefined;
if (this._documentIsDirty || this._config.codeLens.recentChange.enabled) { if (this._documentIsDirty || this._config.codeLens.recentChange.enabled) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange)); blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange));
lenses.push(new GitRecentChangeCodeLens(blameForRangeFn, gitUri, symbol.kind, blameRange, false, line.range.with(new Position(line.range.start.line, startChar)))); lenses.push(new GitRecentChangeCodeLens(blameForRangeFn, gitUri, symbol.kind, blameRange, false, line.range.with(new Position(line.range.start.line, startChar))));
@@ -220,7 +220,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
} }
if (multiline) { if (multiline) {
if (!blameForRangeFn) { if (blameForRangeFn === undefined) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange)); blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange));
} }
if (!this._documentIsDirty) { if (!this._documentIsDirty) {
@@ -250,14 +250,12 @@ export class GitCodeLensProvider implements CodeLensProvider {
title = 'Cannot determine authors (unsaved changes)'; title = 'Cannot determine authors (unsaved changes)';
} }
lens.command = { lens.command = { title: title } as Command;
title: title,
command: undefined
};
return lens; return lens;
} }
const blame = lens.getBlame(); const blame = lens.getBlame();
if (blame === undefined) return lens;
const recentCommit = Iterables.first(blame.commits.values()); const recentCommit = Iterables.first(blame.commits.values());
title = `${recentCommit.author}, ${moment(recentCommit.date).fromNow()}`; title = `${recentCommit.author}, ${moment(recentCommit.date).fromNow()}`;
@@ -280,6 +278,8 @@ export class GitCodeLensProvider implements CodeLensProvider {
_resolveGitAuthorsCodeLens(lens: GitAuthorsCodeLens, token: CancellationToken): CodeLens { _resolveGitAuthorsCodeLens(lens: GitAuthorsCodeLens, token: CancellationToken): CodeLens {
const blame = lens.getBlame(); const blame = lens.getBlame();
if (blame === undefined) return lens;
const count = blame.authors.size; const count = blame.authors.size;
let title = `${count} ${count > 1 ? 'authors' : 'author'} (${Iterables.first(blame.authors.values()).name}${count > 1 ? ' and others' : ''})`; let title = `${count} ${count > 1 ? 'authors' : 'author'} (${Iterables.first(blame.authors.values()).name}${count > 1 ? ' and others' : ''})`;
if (this._config.codeLens.debug) { if (this._config.codeLens.debug) {
@@ -366,7 +366,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = { lens.command = {
title: title, title: title,
command: CodeLensCommand.ShowQuickCommitDetails, command: CodeLensCommand.ShowQuickCommitDetails,
arguments: [Uri.file(lens.uri.fsPath), commit.sha, commit] arguments: [Uri.file(lens.uri.fsPath), commit === undefined ? undefined : commit.sha, commit]
}; };
return lens; return lens;
} }
@@ -375,7 +375,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = { lens.command = {
title: title, title: title,
command: CodeLensCommand.ShowQuickCommitFileDetails, command: CodeLensCommand.ShowQuickCommitFileDetails,
arguments: [Uri.file(lens.uri.fsPath), commit.sha, commit] arguments: [Uri.file(lens.uri.fsPath), commit === undefined ? undefined : commit.sha, commit]
}; };
return lens; return lens;
} }

View File

@@ -15,7 +15,7 @@ export class GitContentProvider implements TextDocumentContentProvider {
const data = GitService.fromGitContentUri(uri); const data = GitService.fromGitContentUri(uri);
const fileName = data.originalFileName || data.fileName; const fileName = data.originalFileName || data.fileName;
try { try {
let text = await this.git.getVersionedFileText(data.repoPath, fileName, data.sha) as string; let text = await this.git.getVersionedFileText(data.repoPath, fileName, data.sha);
if (data.decoration) { if (data.decoration) {
text = `${data.decoration}\n${text}`; text = `${data.decoration}\n${text}`;
} }
@@ -24,7 +24,7 @@ export class GitContentProvider implements TextDocumentContentProvider {
catch (ex) { catch (ex) {
Logger.error(ex, 'GitContentProvider', 'getVersionedFileText'); Logger.error(ex, 'GitContentProvider', 'getVersionedFileText');
await window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`); await window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`);
return undefined; return '';
} }
} }
} }

View File

@@ -15,7 +15,7 @@ import * as path from 'path';
export { GitUri }; export { GitUri };
export * from './git/models/models'; export * from './git/models/models';
export { getNameFromRemoteOpenType, RemoteOpenType } from './git/remotes/provider'; export { getNameFromRemoteOpenType, RemoteOpenType, RemoteProvider } from './git/remotes/provider';
export * from './git/gitContextTracker'; export * from './git/gitContextTracker';
class UriCacheEntry { class UriCacheEntry {
@@ -28,8 +28,9 @@ class GitCacheEntry {
blame?: ICachedBlame; blame?: ICachedBlame;
log?: ICachedLog; log?: ICachedLog;
get hasErrors() { get hasErrors(): boolean {
return !!((this.blame && this.blame.errorMessage) || (this.log && this.log.errorMessage)); return (this.blame !== undefined && this.blame.errorMessage !== undefined) ||
(this.log !== undefined && this.log.errorMessage !== undefined);
} }
constructor(public key: string) { } constructor(public key: string) { }
@@ -69,23 +70,25 @@ export class GitService extends Disposable {
return this._onDidBlameFail.event; return this._onDidBlameFail.event;
} }
private _gitCache: Map<string, GitCacheEntry> | undefined; private _gitCache: Map<string, GitCacheEntry>;
private _remotesCache: Map<string, GitRemote[]>; private _remotesCache: Map<string, GitRemote[]>;
private _cacheDisposable: Disposable | undefined; private _cacheDisposable: Disposable | undefined;
private _uriCache: Map<string, UriCacheEntry> | undefined; private _uriCache: Map<string, UriCacheEntry>;
config: IConfig; config: IConfig;
private _codeLensProvider: GitCodeLensProvider | undefined; private _codeLensProvider: GitCodeLensProvider | undefined;
private _codeLensProviderDisposable: Disposable | undefined; private _codeLensProviderDisposable: Disposable | undefined;
private _disposable: Disposable; private _disposable: Disposable | undefined;
private _fsWatcher: FileSystemWatcher; private _fsWatcher: FileSystemWatcher | undefined;
private _gitignore: Promise<ignore.Ignore>; private _gitignore: Promise<ignore.Ignore>;
static EmptyPromise: Promise<IGitBlame | IGitLog> = Promise.resolve(undefined); static EmptyPromise: Promise<IGitBlame | IGitLog | undefined> = Promise.resolve(undefined);
constructor(private context: ExtensionContext, public repoPath: string) { constructor(private context: ExtensionContext, public repoPath: string) {
super(() => this.dispose()); super(() => this.dispose());
this._gitCache = new Map();
this._remotesCache = new Map();
this._uriCache = new Map(); this._uriCache = new Map();
this._onConfigurationChanged(); this._onConfigurationChanged();
@@ -110,27 +113,24 @@ export class GitService extends Disposable {
this._fsWatcher && this._fsWatcher.dispose(); this._fsWatcher && this._fsWatcher.dispose();
this._fsWatcher = undefined; this._fsWatcher = undefined;
this._gitCache && this._gitCache.clear(); this._gitCache.clear();
this._gitCache = undefined; this._remotesCache.clear();
this._remotesCache && this._remotesCache.clear(); this._uriCache.clear();
this._remotesCache = undefined;
this._uriCache && this._uriCache.clear();
this._uriCache = undefined;
} }
public get UseGitCaching() { public get UseCaching() {
return !!this._gitCache; return this.config.advanced.caching.enabled;
} }
private _onConfigurationChanged() { private _onConfigurationChanged() {
const config = workspace.getConfiguration().get<IConfig>(ExtensionKey); const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
const codeLensChanged = !Objects.areEquivalent(config.codeLens, this.config && this.config.codeLens); const codeLensChanged = !Objects.areEquivalent(cfg.codeLens, this.config && this.config.codeLens);
const advancedChanged = !Objects.areEquivalent(config.advanced, this.config && this.config.advanced); const advancedChanged = !Objects.areEquivalent(cfg.advanced, this.config && this.config.advanced);
if (codeLensChanged) { if (codeLensChanged) {
Logger.log('CodeLens config changed; resetting CodeLens provider'); Logger.log('CodeLens config changed; resetting CodeLens provider');
if (config.codeLens.visibility === CodeLensVisibility.Auto && (config.codeLens.recentChange.enabled || config.codeLens.authors.enabled)) { if (cfg.codeLens.visibility === CodeLensVisibility.Auto && (cfg.codeLens.recentChange.enabled || cfg.codeLens.authors.enabled)) {
if (this._codeLensProvider) { if (this._codeLensProvider) {
this._codeLensProvider.reset(); this._codeLensProvider.reset();
} }
@@ -145,14 +145,11 @@ export class GitService extends Disposable {
this._codeLensProvider = undefined; this._codeLensProvider = undefined;
} }
setCommandContext(CommandContext.CanToggleCodeLens, config.codeLens.visibility !== CodeLensVisibility.Off && (config.codeLens.recentChange.enabled || config.codeLens.authors.enabled)); setCommandContext(CommandContext.CanToggleCodeLens, cfg.codeLens.visibility !== CodeLensVisibility.Off && (cfg.codeLens.recentChange.enabled || cfg.codeLens.authors.enabled));
} }
if (advancedChanged) { if (advancedChanged) {
if (config.advanced.caching.enabled) { if (cfg.advanced.caching.enabled) {
this._gitCache = new Map();
this._remotesCache = new Map();
this._cacheDisposable && this._cacheDisposable.dispose(); this._cacheDisposable && this._cacheDisposable.dispose();
this._fsWatcher = this._fsWatcher || workspace.createFileSystemWatcher('**/.git/index', true, false, true); this._fsWatcher = this._fsWatcher || workspace.createFileSystemWatcher('**/.git/index', true, false, true);
@@ -172,15 +169,12 @@ export class GitService extends Disposable {
this._fsWatcher && this._fsWatcher.dispose(); this._fsWatcher && this._fsWatcher.dispose();
this._fsWatcher = undefined; this._fsWatcher = undefined;
this._gitCache && this._gitCache.clear(); this._gitCache.clear();
this._gitCache = undefined; this._remotesCache.clear();
this._remotesCache && this._remotesCache.clear();
this._remotesCache = undefined;
} }
this._gitignore = new Promise<ignore.Ignore | undefined>((resolve, reject) => { this._gitignore = new Promise<ignore.Ignore | undefined>((resolve, reject) => {
if (!config.advanced.gitignore.enabled) { if (!cfg.advanced.gitignore.enabled) {
resolve(undefined); resolve(undefined);
return; return;
} }
@@ -202,18 +196,18 @@ export class GitService extends Disposable {
}); });
} }
this.config = config; this.config = cfg;
} }
private _onGitChanged() { private _onGitChanged() {
this._gitCache && this._gitCache.clear(); this._gitCache.clear();
this._onDidChangeGitCache.fire(); this._onDidChangeGitCache.fire();
this._codeLensProvider && this._codeLensProvider.reset(); this._codeLensProvider && this._codeLensProvider.reset();
} }
private _removeCachedEntry(document: TextDocument, reason: RemoveCacheReason) { private _removeCachedEntry(document: TextDocument, reason: RemoveCacheReason) {
if (!this.UseGitCaching) return; if (!this.UseCaching) return;
if (document.uri.scheme !== DocumentSchemes.File) return; if (document.uri.scheme !== DocumentSchemes.File) return;
const cacheKey = this.getCacheEntryKey(document.fileName); const cacheKey = this.getCacheEntryKey(document.fileName);
@@ -240,21 +234,21 @@ export class GitService extends Disposable {
return await new Promise<boolean>((resolve, reject) => fs.exists(path.resolve(repoPath, fileName), e => resolve(e))); return await new Promise<boolean>((resolve, reject) => fs.exists(path.resolve(repoPath, fileName), e => resolve(e)));
} }
async findNextCommit(repoPath: string, fileName: string, sha?: string): Promise<GitLogCommit> { async findNextCommit(repoPath: string, fileName: string, sha?: string): Promise<GitLogCommit | undefined> {
let log = await this.getLogForFile(repoPath, fileName, sha, 1, undefined, true); let log = await this.getLogForFile(repoPath, fileName, sha, 1, undefined, true);
let commit = log && Iterables.first(log.commits.values()); let commit = log && Iterables.first(log.commits.values());
if (commit) return commit; if (commit) return commit;
fileName = await this.findNextFileName(repoPath, fileName, sha); const nextFileName = await this.findNextFileName(repoPath, fileName, sha);
if (fileName) { if (nextFileName) {
log = await this.getLogForFile(repoPath, fileName, sha, 1, undefined, true); log = await this.getLogForFile(repoPath, nextFileName, sha, 1, undefined, true);
commit = log && Iterables.first(log.commits.values()); commit = log && Iterables.first(log.commits.values());
} }
return commit; return commit;
} }
async findNextFileName(repoPath: string, fileName: string, sha?: string): Promise<string> { async findNextFileName(repoPath: string | undefined, fileName: string, sha?: string): Promise<string | undefined> {
[fileName, repoPath] = Git.splitPath(fileName, repoPath); [fileName, repoPath] = Git.splitPath(fileName, repoPath);
return (await this._fileExists(repoPath, fileName)) return (await this._fileExists(repoPath, fileName))
@@ -262,7 +256,7 @@ export class GitService extends Disposable {
: await this._findNextFileName(repoPath, fileName, sha); : await this._findNextFileName(repoPath, fileName, sha);
} }
async _findNextFileName(repoPath: string, fileName: string, sha?: string): Promise<string> { async _findNextFileName(repoPath: string, fileName: string, sha?: string): Promise<string | undefined> {
if (sha === undefined) { if (sha === undefined) {
// Get the most recent commit for this file name // Get the most recent commit for this file name
const c = await this.getLogCommit(repoPath, fileName); const c = await this.getLogCommit(repoPath, fileName);
@@ -282,12 +276,14 @@ export class GitService extends Disposable {
return status.fileName; return status.fileName;
} }
async findWorkingFileName(commit: GitCommit): Promise<string>; async findWorkingFileName(commit: GitCommit): Promise<string | undefined>;
async findWorkingFileName(repoPath: string, fileName: string): Promise<string>; async findWorkingFileName(repoPath: string | undefined, fileName: string): Promise<string | undefined>;
async findWorkingFileName(commitOrRepoPath: GitCommit | string, fileName?: string): Promise<string> { async findWorkingFileName(commitOrRepoPath: GitCommit | string | undefined, fileName?: string): Promise<string | undefined> {
let repoPath: string; let repoPath: string | undefined;
if (typeof commitOrRepoPath === 'string') { if (commitOrRepoPath === undefined || typeof commitOrRepoPath === 'string') {
repoPath = commitOrRepoPath; repoPath = commitOrRepoPath;
if (fileName === undefined) throw new Error('Invalid fileName');
[fileName] = Git.splitPath(fileName, repoPath); [fileName] = Git.splitPath(fileName, repoPath);
} }
else { else {
@@ -298,15 +294,15 @@ export class GitService extends Disposable {
} }
while (true) { while (true) {
if (await this._fileExists(repoPath, fileName)) return fileName; if (await this._fileExists(repoPath!, fileName)) return fileName;
fileName = await this._findNextFileName(repoPath, fileName); fileName = await this._findNextFileName(repoPath!, fileName);
if (fileName === undefined) return undefined; if (fileName === undefined) return undefined;
} }
} }
public async getBlameability(uri: GitUri): Promise<boolean> { public async getBlameability(uri: GitUri): Promise<boolean> {
if (!this.UseGitCaching) return await this.isTracked(uri); if (!this.UseCaching) return await this.isTracked(uri);
const cacheKey = this.getCacheEntryKey(uri.fsPath); const cacheKey = this.getCacheEntryKey(uri.fsPath);
const entry = this._gitCache.get(cacheKey); const entry = this._gitCache.get(cacheKey);
@@ -321,7 +317,7 @@ export class GitService extends Disposable {
const fileName = uri.fsPath; const fileName = uri.fsPath;
let entry: GitCacheEntry | undefined; let entry: GitCacheEntry | undefined;
if (this.UseGitCaching && !uri.sha) { if (this.UseCaching && !uri.sha) {
const cacheKey = this.getCacheEntryKey(fileName); const cacheKey = this.getCacheEntryKey(fileName);
entry = this._gitCache.get(cacheKey); entry = this._gitCache.get(cacheKey);
@@ -347,7 +343,7 @@ export class GitService extends Disposable {
return promise; return promise;
} }
private async _getBlameForFile(uri: GitUri, fileName: string, entry: GitCacheEntry | undefined): Promise<IGitBlame> { private async _getBlameForFile(uri: GitUri, fileName: string, entry: GitCacheEntry | undefined): Promise<IGitBlame | undefined> {
const [file, root] = Git.splitPath(fileName, uri.repoPath, false); const [file, root] = Git.splitPath(fileName, uri.repoPath, false);
const ignore = await this._gitignore; const ignore = await this._gitignore;
@@ -387,12 +383,16 @@ export class GitService extends Disposable {
async getBlameForLine(uri: GitUri, line: number): Promise<IGitBlameLine | undefined> { async getBlameForLine(uri: GitUri, line: number): Promise<IGitBlameLine | undefined> {
Logger.log(`getBlameForLine('${uri.repoPath}', '${uri.fsPath}', ${line}, ${uri.sha})`); Logger.log(`getBlameForLine('${uri.repoPath}', '${uri.fsPath}', ${line}, ${uri.sha})`);
if (this.UseGitCaching && !uri.sha) { if (this.UseCaching && !uri.sha) {
const blame = await this.getBlameForFile(uri); const blame = await this.getBlameForFile(uri);
const blameLine = blame && blame.lines[line]; if (blame === undefined) return undefined;
if (!blameLine) return undefined;
const blameLine = blame.lines[line];
if (blameLine === undefined) return undefined;
const commit = blame.commits.get(blameLine.sha); const commit = blame.commits.get(blameLine.sha);
if (commit === undefined) return undefined;
return { return {
author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }), author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }),
commit: commit, commit: commit,
@@ -454,7 +454,7 @@ export class GitService extends Disposable {
commits.set(c.sha, commit); commits.set(c.sha, commit);
let author = authors.get(commit.author); let author = authors.get(commit.author);
if (!author) { if (author === undefined) {
author = { author = {
name: commit.author, name: commit.author,
lineCount: 0 lineCount: 0
@@ -494,14 +494,14 @@ export class GitService extends Disposable {
const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration); const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration);
locations.push(new Location(uri, new Position(0, 0))); locations.push(new Location(uri, new Position(0, 0)));
if (c.sha === selectedSha) { if (c.sha === selectedSha) {
locations.push(new Location(uri, new Position(line + 1, 0))); locations.push(new Location(uri, new Position((line || 0) + 1, 0)));
} }
}); });
return locations; return locations;
} }
async getBranch(repoPath: string): Promise<GitBranch> { async getBranch(repoPath: string): Promise<GitBranch | undefined> {
Logger.log(`getBranch('${repoPath}')`); Logger.log(`getBranch('${repoPath}')`);
const data = await Git.branch(repoPath, false); const data = await Git.branch(repoPath, false);
@@ -533,10 +533,10 @@ export class GitService extends Disposable {
return entry && entry.uri; return entry && entry.uri;
} }
async getLogCommit(repoPath: string, fileName: string, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise<GitLogCommit | undefined>; async getLogCommit(repoPath: string | undefined, fileName: string, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise<GitLogCommit | undefined>;
async getLogCommit(repoPath: string, fileName: string, sha: string, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise<GitLogCommit | undefined>; async getLogCommit(repoPath: string | undefined, fileName: string, sha: string | undefined, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise<GitLogCommit | undefined>;
async getLogCommit(repoPath: string, fileName: string, shaOrOptions?: string | { firstIfMissing?: boolean, previous?: boolean }, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise<GitLogCommit | undefined> { async getLogCommit(repoPath: string | undefined, fileName: string, shaOrOptions?: string | undefined | { firstIfMissing?: boolean, previous?: boolean }, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise<GitLogCommit | undefined> {
let sha: string; let sha: string | undefined = undefined;
if (typeof shaOrOptions === 'string') { if (typeof shaOrOptions === 'string') {
sha = shaOrOptions; sha = shaOrOptions;
} }
@@ -578,7 +578,7 @@ export class GitService extends Disposable {
maxCount = this.config.advanced.maxQuickHistory || 0; maxCount = this.config.advanced.maxQuickHistory || 0;
} }
let searchArgs: string[]; let searchArgs: string[] | undefined = undefined;
switch (searchBy) { switch (searchBy) {
case GitRepoSearchBy.Author: case GitRepoSearchBy.Author:
searchArgs = [`--author=${search}`]; searchArgs = [`--author=${search}`];
@@ -604,11 +604,11 @@ export class GitService extends Disposable {
} }
} }
getLogForFile(repoPath: string, fileName: string, sha?: string, maxCount?: number, range?: Range, reverse: boolean = false): Promise<IGitLog | undefined> { getLogForFile(repoPath: string | undefined, fileName: string, sha?: string, maxCount?: number, range?: Range, reverse: boolean = false): Promise<IGitLog | undefined> {
Logger.log(`getLogForFile('${repoPath}', '${fileName}', ${sha}, ${maxCount}, ${range && `[${range.start.line}, ${range.end.line}]`}, ${reverse})`); Logger.log(`getLogForFile('${repoPath}', '${fileName}', ${sha}, ${maxCount}, ${range && `[${range.start.line}, ${range.end.line}]`}, ${reverse})`);
let entry: GitCacheEntry | undefined; let entry: GitCacheEntry | undefined;
if (this.UseGitCaching && !sha && !range && !maxCount && !reverse) { if (this.UseCaching && !sha && !range && !maxCount && !reverse) {
const cacheKey = this.getCacheEntryKey(fileName); const cacheKey = this.getCacheEntryKey(fileName);
entry = this._gitCache.get(cacheKey); entry = this._gitCache.get(cacheKey);
@@ -634,7 +634,7 @@ export class GitService extends Disposable {
return promise; return promise;
} }
private async _getLogForFile(repoPath: string, fileName: string, sha: string, range: Range, maxCount: number, reverse: boolean, entry: GitCacheEntry | undefined): Promise<IGitLog> { private async _getLogForFile(repoPath: string | undefined, fileName: string, sha: string | undefined, range: Range | undefined, maxCount: number | undefined, reverse: boolean, entry: GitCacheEntry | undefined): Promise<IGitLog | undefined> {
const [file, root] = Git.splitPath(fileName, repoPath, false); const [file, root] = Git.splitPath(fileName, repoPath, false);
const ignore = await this._gitignore; const ignore = await this._gitignore;
@@ -683,7 +683,7 @@ export class GitService extends Disposable {
const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration); const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration);
locations.push(new Location(uri, new Position(0, 0))); locations.push(new Location(uri, new Position(0, 0)));
if (c.sha === selectedSha) { if (c.sha === selectedSha) {
locations.push(new Location(uri, new Position(line + 1, 0))); locations.push(new Location(uri, new Position((line || 0) + 1, 0)));
} }
}); });
@@ -696,21 +696,21 @@ export class GitService extends Disposable {
Logger.log(`getRemotes('${repoPath}')`); Logger.log(`getRemotes('${repoPath}')`);
if (this.UseGitCaching) { if (this.UseCaching) {
const remotes = this._remotesCache.get(repoPath); const remotes = this._remotesCache.get(repoPath);
if (remotes !== undefined) return remotes; if (remotes !== undefined) return remotes;
} }
const data = await Git.remote(repoPath); const data = await Git.remote(repoPath);
const remotes = data.split('\n').filter(_ => !!_).map(_ => new GitRemote(_)); const remotes = data.split('\n').filter(_ => !!_).map(_ => new GitRemote(_));
if (this.UseGitCaching) { if (this.UseCaching) {
this._remotesCache.set(repoPath, remotes); this._remotesCache.set(repoPath, remotes);
} }
return remotes; return remotes;
} }
getRepoPath(cwd: string): Promise<string> { getRepoPath(cwd: string): Promise<string> {
return Git.getRepoPath(cwd); return GitService.getRepoPath(cwd);
} }
async getRepoPathFromFile(fileName: string): Promise<string | undefined> { async getRepoPathFromFile(fileName: string): Promise<string | undefined> {
@@ -724,24 +724,26 @@ export class GitService extends Disposable {
return (await GitUri.fromUri(uri, this)).repoPath || this.repoPath; return (await GitUri.fromUri(uri, this)).repoPath || this.repoPath;
} }
async getStashList(repoPath: string): Promise<IGitStash> { async getStashList(repoPath: string): Promise<IGitStash | undefined> {
Logger.log(`getStash('${repoPath}')`); Logger.log(`getStash('${repoPath}')`);
const data = await Git.stash_list(repoPath); const data = await Git.stash_list(repoPath);
return GitStashParser.parse(data, repoPath); return GitStashParser.parse(data, repoPath);
} }
async getStatusForFile(repoPath: string, fileName: string): Promise<GitStatusFile> { async getStatusForFile(repoPath: string, fileName: string): Promise<GitStatusFile | undefined> {
Logger.log(`getStatusForFile('${repoPath}', '${fileName}')`); Logger.log(`getStatusForFile('${repoPath}', '${fileName}')`);
const porcelainVersion = Git.validateVersion(2, 11) ? 2 : 1; const porcelainVersion = Git.validateVersion(2, 11) ? 2 : 1;
const data = await Git.status_file(repoPath, fileName, porcelainVersion); const data = await Git.status_file(repoPath, fileName, porcelainVersion);
const status = GitStatusParser.parse(data, repoPath, porcelainVersion); const status = GitStatusParser.parse(data, repoPath, porcelainVersion);
return status && status.files.length && status.files[0]; if (status === undefined || !status.files.length) return undefined;
return status.files[0];
} }
async getStatusForRepo(repoPath: string): Promise<IGitStatus> { async getStatusForRepo(repoPath: string): Promise<IGitStatus | undefined> {
Logger.log(`getStatusForRepo('${repoPath}')`); Logger.log(`getStatusForRepo('${repoPath}')`);
const porcelainVersion = Git.validateVersion(2, 11) ? 2 : 1; const porcelainVersion = Git.validateVersion(2, 11) ? 2 : 1;
@@ -750,12 +752,12 @@ export class GitService extends Disposable {
return GitStatusParser.parse(data, repoPath, porcelainVersion); return GitStatusParser.parse(data, repoPath, porcelainVersion);
} }
async getVersionedFile(repoPath: string, fileName: string, sha: string) { async getVersionedFile(repoPath: string | undefined, fileName: string, sha: string) {
Logger.log(`getVersionedFile('${repoPath}', '${fileName}', ${sha})`); Logger.log(`getVersionedFile('${repoPath}', '${fileName}', ${sha})`);
const file = await Git.getVersionedFile(repoPath, fileName, sha); const file = await Git.getVersionedFile(repoPath, fileName, sha);
const cacheKey = this.getCacheEntryKey(file); const cacheKey = this.getCacheEntryKey(file);
const entry = new UriCacheEntry(new GitUri(Uri.file(fileName), { sha, repoPath, fileName })); const entry = new UriCacheEntry(new GitUri(Uri.file(fileName), { sha, repoPath: repoPath!, fileName }));
this._uriCache.set(cacheKey, entry); this._uriCache.set(cacheKey, entry);
return file; return file;
} }
@@ -792,14 +794,14 @@ export class GitService extends Disposable {
async isFileUncommitted(uri: GitUri): Promise<boolean> { async isFileUncommitted(uri: GitUri): Promise<boolean> {
Logger.log(`isFileUncommitted('${uri.repoPath}', '${uri.fsPath}')`); Logger.log(`isFileUncommitted('${uri.repoPath}', '${uri.fsPath}')`);
const status = await this.getStatusForFile(uri.repoPath, uri.fsPath); const status = await this.getStatusForFile(uri.repoPath!, uri.fsPath);
return !!status; return !!status;
} }
async isTracked(uri: GitUri): Promise<boolean> { async isTracked(uri: GitUri): Promise<boolean> {
Logger.log(`isFileUncommitted('${uri.repoPath}', '${uri.fsPath}')`); Logger.log(`isFileUncommitted('${uri.repoPath}', '${uri.fsPath}')`);
const result = await Git.ls_files(uri.repoPath, uri.fsPath); const result = await Git.ls_files(uri.repoPath === undefined ? '' : uri.repoPath, uri.fsPath);
return !!result; return !!result;
} }
@@ -849,7 +851,7 @@ export class GitService extends Disposable {
return Git.gitInfo().version; return Git.gitInfo().version;
} }
static getRepoPath(cwd: string): Promise<string> { static getRepoPath(cwd: string | undefined): Promise<string> {
return Git.getRepoPath(cwd); return Git.getRepoPath(cwd);
} }
@@ -874,15 +876,15 @@ export class GitService extends Disposable {
return Git.normalizePath(fileName, repoPath); return Git.normalizePath(fileName, repoPath);
} }
static toGitContentUri(sha: string, shortSha: string, fileName: string, repoPath: string, originalFileName: string): Uri; static toGitContentUri(sha: string, shortSha: string, fileName: string, repoPath: string, originalFileName?: string): Uri;
static toGitContentUri(commit: GitCommit): Uri; static toGitContentUri(commit: GitCommit): Uri;
static toGitContentUri(shaOrcommit: string | GitCommit, shortSha?: string, fileName?: string, repoPath?: string, originalFileName?: string): Uri { static toGitContentUri(shaOrcommit: string | GitCommit, shortSha?: string, fileName?: string, repoPath?: string, originalFileName?: string): Uri {
let data: IGitUriData; let data: IGitUriData;
if (typeof shaOrcommit === 'string') { if (typeof shaOrcommit === 'string') {
data = GitService._toGitUriData({ data = GitService._toGitUriData({
sha: shaOrcommit, sha: shaOrcommit,
fileName: fileName, fileName: fileName!,
repoPath: repoPath, repoPath: repoPath!,
originalFileName: originalFileName originalFileName: originalFileName
}); });
} }
@@ -892,8 +894,8 @@ export class GitService extends Disposable {
shortSha = shaOrcommit.shortSha; shortSha = shaOrcommit.shortSha;
} }
const extension = path.extname(fileName); const extension = path.extname(fileName!);
return Uri.parse(`${DocumentSchemes.GitLensGit}:${path.basename(fileName, extension)}:${shortSha}${extension}?${JSON.stringify(data)}`); return Uri.parse(`${DocumentSchemes.GitLensGit}:${path.basename(fileName!, extension)}:${shortSha}${extension}?${JSON.stringify(data)}`);
} }
static toReferenceGitContentUri(commit: GitCommit, index: number, commitCount: number, originalFileName?: string, decoration?: string): Uri { static toReferenceGitContentUri(commit: GitCommit, index: number, commitCount: number, originalFileName?: string, decoration?: string): Uri {
@@ -911,7 +913,7 @@ export class GitService extends Disposable {
} }
// NOTE: Need to specify an index here, since I can't control the sort order -- just alphabetic or by file location // NOTE: Need to specify an index here, since I can't control the sort order -- just alphabetic or by file location
return Uri.parse(`${scheme}:${pad(data.index)} \u2022 ${encodeURIComponent(message)} \u2022 ${moment(commit.date).format('MMM D, YYYY hh:MMa')} \u2022 ${encodeURIComponent(uriPath)}?${JSON.stringify(data)}`); return Uri.parse(`${scheme}:${pad(data.index || 0)} \u2022 ${encodeURIComponent(message)} \u2022 ${moment(commit.date).format('MMM D, YYYY hh:MMa')} \u2022 ${encodeURIComponent(uriPath)}?${JSON.stringify(data)}`);
} }
private static _toGitUriData<T extends IGitUriData>(commit: IGitUriData, index?: number, originalFileName?: string, decoration?: string): T { private static _toGitUriData<T extends IGitUriData>(commit: IGitUriData, index?: number, originalFileName?: string, decoration?: string): T {

View File

@@ -19,6 +19,7 @@ let output: OutputChannel;
function onConfigurationChanged() { function onConfigurationChanged() {
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey); const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey);
if (cfg === undefined) return;
if (cfg.debug !== debug || cfg.outputLevel !== level) { if (cfg.debug !== debug || cfg.outputLevel !== level) {
debug = cfg.debug; debug = cfg.debug;

View File

@@ -35,7 +35,7 @@ export class BranchHistoryQuickPick {
description: `\u00a0 \u2014 \u00a0\u00a0 search for commits by message, author, files, or commit id` description: `\u00a0 \u2014 \u00a0\u00a0 search for commits by message, author, files, or commit id`
}, Commands.ShowCommitSearch, [new GitUri(Uri.file(log.repoPath), { fileName: '', repoPath: log.repoPath }), undefined, undefined, currentCommand])); }, Commands.ShowCommitSearch, [new GitUri(Uri.file(log.repoPath), { fileName: '', repoPath: log.repoPath }), undefined, undefined, currentCommand]));
let previousPageCommand: CommandQuickPickItem; let previousPageCommand: CommandQuickPickItem | undefined = undefined;
if (log.truncated || log.sha) { if (log.truncated || log.sha) {
if (log.truncated) { if (log.truncated) {
@@ -72,13 +72,14 @@ export class BranchHistoryQuickPick {
}, Commands.ShowQuickBranchHistory, [uri, branch, log.maxCount, goBackCommand, undefined, nextPageCommand]); }, Commands.ShowQuickBranchHistory, [uri, branch, log.maxCount, goBackCommand, undefined, nextPageCommand]);
const last = Iterables.last(log.commits.values()); const last = Iterables.last(log.commits.values());
if (last != null) {
previousPageCommand = new CommandQuickPickItem({
label: `$(arrow-left) Show Previous Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows ${log.maxCount} older commits`
}, Commands.ShowQuickBranchHistory, [new GitUri(uri ? uri : last.uri, last), branch, log.maxCount, goBackCommand, undefined, npc]);
previousPageCommand = new CommandQuickPickItem({ items.splice(0, 0, previousPageCommand);
label: `$(arrow-left) Show Previous Commits`, }
description: `\u00a0 \u2014 \u00a0\u00a0 shows ${log.maxCount} older commits`
}, Commands.ShowQuickBranchHistory, [new GitUri(uri ? uri : last.uri, last), branch, log.maxCount, goBackCommand, undefined, npc]);
items.splice(0, 0, previousPageCommand);
} }
} }

View File

@@ -11,7 +11,7 @@ export class BranchQuickPickItem implements QuickPickItem {
constructor(public branch: GitBranch) { constructor(public branch: GitBranch) {
this.label = `${branch.current ? '$(check)\u00a0' : '\u00a0\u00a0\u00a0\u00a0'} ${branch.name}`; this.label = `${branch.current ? '$(check)\u00a0' : '\u00a0\u00a0\u00a0\u00a0'} ${branch.name}`;
this.description = branch.remote ? '\u00a0\u00a0 remote branch' : null; this.description = branch.remote ? '\u00a0\u00a0 remote branch' : '';
} }
} }

View File

@@ -19,20 +19,20 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
constructor(commit: GitCommit, status: IGitStatusFile) { constructor(commit: GitCommit, status: IGitStatusFile) {
const icon = getGitStatusIcon(status.status); const icon = getGitStatusIcon(status.status);
let directory = GitService.normalizePath(path.dirname(status.fileName)); let directory: string | undefined = GitService.normalizePath(path.dirname(status.fileName));
if (!directory || directory === '.') { if (!directory || directory === '.') {
directory = undefined; directory = '';
} }
const description = (status.status === 'R' && status.originalFileName) const description = (status.status === 'R' && status.originalFileName)
? `${directory || ''} \u00a0\u2190\u00a0 ${status.originalFileName}` ? `${directory} \u00a0\u2190\u00a0 ${status.originalFileName}`
: directory; : directory;
let sha; let sha;
let shortSha; let shortSha;
if (status.status === 'D') { if (status.status === 'D') {
sha = commit.previousSha; sha = commit.previousSha!;
shortSha = commit.previousShortSha; shortSha = commit.previousShortSha!;
} }
else { else {
sha = commit.sha; sha = commit.sha;
@@ -56,7 +56,7 @@ export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPi
constructor(commit: GitLogCommit, item?: QuickPickItem) { constructor(commit: GitLogCommit, item?: QuickPickItem) {
const uris = commit.fileStatuses.map(s => (s.status === 'D') const uris = commit.fileStatuses.map(s => (s.status === 'D')
? GitService.toGitContentUri(commit.previousSha, commit.previousShortSha, s.fileName, commit.repoPath, s.originalFileName) ? GitService.toGitContentUri(commit.previousSha!, commit.previousShortSha!, s.fileName, commit.repoPath, s.originalFileName)
: GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName)); : GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName));
super(uris, item || { super(uris, item || {
@@ -74,7 +74,7 @@ export class OpenCommitWorkingTreeFilesCommandQuickPickItem extends OpenFilesCom
const uris = commit.fileStatuses.filter(_ => _.status !== 'D').map(_ => GitUri.fromFileStatus(_, repoPath)); const uris = commit.fileStatuses.filter(_ => _.status !== 'D').map(_ => GitUri.fromFileStatus(_, repoPath));
super(uris, item || { super(uris, item || {
label: `$(file-symlink-file) Open Changed Working Files`, label: `$(file-symlink-file) Open Changed Working Files`,
description: undefined description: ''
//detail: `Opens all of the changed file in the working tree` //detail: `Opens all of the changed file in the working tree`
}); });
} }
@@ -142,13 +142,13 @@ export class CommitDetailsQuickPick {
items.splice(0, 0, goBackCommand); items.splice(0, 0, goBackCommand);
} }
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>); let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>) | undefined = undefined;
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>); let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>) | undefined = undefined;
if (!stash) { if (!stash) {
// If we have the full history, we are good // If we have the full history, we are good
if (repoLog && !repoLog.truncated && !repoLog.sha) { if (repoLog && !repoLog.truncated && !repoLog.sha) {
previousCommand = commit.previousSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, repoLog]); previousCommand = commit.previousSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, repoLog]);
nextCommand = commit.nextSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, repoLog]); nextCommand = commit.nextSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, repoLog]);
} }
else { else {
previousCommand = async () => { previousCommand = async () => {

View File

@@ -3,7 +3,7 @@ import { Arrays, Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard, KeyNoopCommand } from '../commands'; import { Commands, Keyboard, KeyNoopCommand } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './common'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem } from './common';
import { GitLogCommit, GitService, GitUri, IGitLog } from '../gitService'; import { GitBranch, GitLogCommit, GitService, GitUri, IGitLog } from '../gitService';
import { OpenRemotesCommandQuickPickItem } from './remotes'; import { OpenRemotesCommandQuickPickItem } from './remotes';
import * as moment from 'moment'; import * as moment from 'moment';
import * as path from 'path'; import * as path from 'path';
@@ -14,7 +14,7 @@ export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPick
let description: string; let description: string;
let uri: Uri; let uri: Uri;
if (commit.status === 'D') { if (commit.status === 'D') {
uri = GitService.toGitContentUri(commit.previousSha, commit.previousShortSha, commit.previousFileName, commit.repoPath, undefined); uri = GitService.toGitContentUri(commit.previousSha!, commit.previousShortSha!, commit.previousFileName!, commit.repoPath, undefined);
description = `\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)} in \u00a0$(git-commit) ${commit.previousShortSha} (deleted in \u00a0$(git-commit) ${commit.shortSha})`; description = `\u00a0 \u2014 \u00a0\u00a0 ${path.basename(commit.fileName)} in \u00a0$(git-commit) ${commit.previousShortSha} (deleted in \u00a0$(git-commit) ${commit.shortSha})`;
} }
else { else {
@@ -51,8 +51,10 @@ export class CommitFileDetailsQuickPick {
const isUncommitted = commit.isUncommitted; const isUncommitted = commit.isUncommitted;
if (isUncommitted) { if (isUncommitted) {
// Since we can't trust the previous sha on an uncommitted commit, find the last commit for this file // Since we can't trust the previous sha on an uncommitted commit, find the last commit for this file
commit = await git.getLogCommit(undefined, commit.uri.fsPath, { previous: true }); const c = await git.getLogCommit(undefined, commit.uri.fsPath, { previous: true });
if (!commit) return undefined; if (c === undefined) return undefined;
commit = c;
} }
if (!stash) { if (!stash) {
@@ -99,7 +101,7 @@ export class CommitFileDetailsQuickPick {
items.push(new OpenRemotesCommandQuickPickItem(remotes, 'file', commit.fileName, undefined, commit, currentCommand)); items.push(new OpenRemotesCommandQuickPickItem(remotes, 'file', commit.fileName, undefined, commit, currentCommand));
} }
if (commit.workingFileName && commit.status !== 'D') { if (commit.workingFileName && commit.status !== 'D') {
const branch = await git.getBranch(commit.repoPath || git.repoPath); const branch = await git.getBranch(commit.repoPath || git.repoPath) as GitBranch;
items.push(new OpenRemotesCommandQuickPickItem(remotes, 'working-file', commit.workingFileName, branch.name, undefined, currentCommand)); items.push(new OpenRemotesCommandQuickPickItem(remotes, 'working-file', commit.workingFileName, branch.name, undefined, currentCommand));
} }
} }
@@ -122,13 +124,13 @@ export class CommitFileDetailsQuickPick {
items.splice(0, 0, goBackCommand); items.splice(0, 0, goBackCommand);
} }
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>); let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>) | undefined = undefined;
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>); let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>) | undefined = undefined;
if (!stash) { if (!stash) {
// If we have the full history, we are good // If we have the full history, we are good
if (fileLog && !fileLog.truncated && !fileLog.sha) { if (fileLog && !fileLog.truncated && !fileLog.sha) {
previousCommand = commit.previousSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, fileLog]); previousCommand = commit.previousSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, fileLog]);
nextCommand = commit.nextSha && new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, fileLog]); nextCommand = commit.nextSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, fileLog]);
} }
else { else {
previousCommand = async () => { previousCommand = async () => {
@@ -138,6 +140,8 @@ export class CommitFileDetailsQuickPick {
// If we can't find the commit or the previous commit isn't available (since it isn't trustworthy) // If we can't find the commit or the previous commit isn't available (since it isn't trustworthy)
if (!c || !c.previousSha) { if (!c || !c.previousSha) {
log = await git.getLogForFile(commit.repoPath, uri.fsPath, commit.sha, git.config.advanced.maxQuickHistory); log = await git.getLogForFile(commit.repoPath, uri.fsPath, commit.sha, git.config.advanced.maxQuickHistory);
if (log === undefined) return KeyNoopCommand;
c = log && log.commits.get(commit.sha); c = log && log.commits.get(commit.sha);
// Since we exclude merge commits in file log, just grab the first returned commit // Since we exclude merge commits in file log, just grab the first returned commit
if (!c && commit.isMerge) { if (!c && commit.isMerge) {

View File

@@ -8,7 +8,8 @@ import { GitCommit, GitLogCommit, GitStashCommit } from '../gitService';
import * as moment from 'moment'; import * as moment from 'moment';
export function getQuickPickIgnoreFocusOut() { export function getQuickPickIgnoreFocusOut() {
return !workspace.getConfiguration(ExtensionKey).get<IAdvancedConfig>('advanced').quickPick.closeOnFocusOut; const cfg = workspace.getConfiguration(ExtensionKey).get<IAdvancedConfig>('advanced')!;
return !cfg.quickPick.closeOnFocusOut;
} }
export function showQuickPickProgress(message: string, mapping?: KeyMapping, delay: boolean = false): CancellationTokenSource { export function showQuickPickProgress(message: string, mapping?: KeyMapping, delay: boolean = false): CancellationTokenSource {
@@ -32,7 +33,7 @@ export function showQuickPickProgress(message: string, mapping?: KeyMapping, del
async function _showQuickPickProgress(message: string, cancellation: CancellationTokenSource, mapping?: KeyMapping) { async function _showQuickPickProgress(message: string, cancellation: CancellationTokenSource, mapping?: KeyMapping) {
// Logger.log(`showQuickPickProgress`, `show`, message); // Logger.log(`showQuickPickProgress`, `show`, message);
const scope: KeyboardScope = mapping && await Keyboard.instance.beginScope(mapping); const scope: KeyboardScope | undefined = mapping && await Keyboard.instance.beginScope(mapping);
try { try {
await window.showQuickPick(_getInfiniteCancellablePromise(cancellation), { await window.showQuickPick(_getInfiniteCancellablePromise(cancellation), {
@@ -64,21 +65,23 @@ export class CommandQuickPickItem implements QuickPickItem {
label: string; label: string;
description: string; description: string;
detail: string; detail?: string | undefined;
constructor(item: QuickPickItem, protected command: Commands, protected args?: any[]) { constructor(item: QuickPickItem, protected command: Commands | undefined, protected args?: any[]) {
Object.assign(this, item); Object.assign(this, item);
} }
execute(): Thenable<{}> { execute(): Thenable<{} | undefined> {
if (this.command === undefined) return Promise.resolve(undefined);
return commands.executeCommand(this.command, ...(this.args || [])); return commands.executeCommand(this.command, ...(this.args || []));
} }
} }
export class KeyCommandQuickPickItem extends CommandQuickPickItem { export class KeyCommandQuickPickItem extends CommandQuickPickItem {
constructor(protected command: Commands, protected args?: any[]) { constructor(command: Commands, args?: any[]) {
super({ label: undefined, description: undefined }, command, args); super({ label: '', description: '' } as QuickPickItem, command, args);
} }
} }
@@ -88,7 +91,7 @@ export class OpenFileCommandQuickPickItem extends CommandQuickPickItem {
super(item, undefined, undefined); super(item, undefined, undefined);
} }
async execute(pinned: boolean = false): Promise<{}> { async execute(pinned: boolean = false): Promise<{} | undefined> {
return this.open(pinned); return this.open(pinned);
} }
@@ -103,7 +106,7 @@ export class OpenFilesCommandQuickPickItem extends CommandQuickPickItem {
super(item, undefined, undefined); super(item, undefined, undefined);
} }
async execute(): Promise<{}> { async execute(): Promise<{} | undefined> {
for (const uri of this.uris) { for (const uri of this.uris) {
await openEditor(uri, true); await openEditor(uri, true);
} }
@@ -126,7 +129,7 @@ export class CommitQuickPickItem implements QuickPickItem {
if (commit instanceof GitStashCommit) { if (commit instanceof GitStashCommit) {
this.label = `${commit.stashName}\u00a0\u2022\u00a0${message}`; this.label = `${commit.stashName}\u00a0\u2022\u00a0${message}`;
this.description = null; this.description = '';
this.detail = `\u00a0 ${moment(commit.date).fromNow()}\u00a0\u00a0\u2022\u00a0 ${commit.getDiffStatus()}`; this.detail = `\u00a0 ${moment(commit.date).fromNow()}\u00a0\u00a0\u2022\u00a0 ${commit.getDiffStatus()}`;
} }
else { else {

View File

@@ -21,7 +21,7 @@ export class FileHistoryQuickPick {
static async show(git: GitService, log: IGitLog, uri: GitUri, progressCancellation: CancellationTokenSource, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> { static async show(git: GitService, log: IGitLog, uri: GitUri, progressCancellation: CancellationTokenSource, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem): Promise<CommitQuickPickItem | CommandQuickPickItem | undefined> {
const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[]; const items = Array.from(Iterables.map(log.commits.values(), c => new CommitQuickPickItem(c))) as (CommitQuickPickItem | CommandQuickPickItem)[];
let previousPageCommand: CommandQuickPickItem; let previousPageCommand: CommandQuickPickItem | undefined = undefined;
let index = 0; let index = 0;
if (log.truncated || log.sha) { if (log.truncated || log.sha) {
@@ -63,18 +63,19 @@ export class FileHistoryQuickPick {
}, Commands.ShowQuickFileHistory, [uri, undefined, log.maxCount, goBackCommand, undefined, nextPageCommand]); }, Commands.ShowQuickFileHistory, [uri, undefined, log.maxCount, goBackCommand, undefined, nextPageCommand]);
const last = Iterables.last(log.commits.values()); const last = Iterables.last(log.commits.values());
if (last != null) {
previousPageCommand = new CommandQuickPickItem({
label: `$(arrow-left) Show Previous Commits`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows ${log.maxCount} older commits`
}, Commands.ShowQuickFileHistory, [new GitUri(uri, last), undefined, log.maxCount, goBackCommand, undefined, npc]);
previousPageCommand = new CommandQuickPickItem({ index++;
label: `$(arrow-left) Show Previous Commits`, items.splice(0, 0, previousPageCommand);
description: `\u00a0 \u2014 \u00a0\u00a0 shows ${log.maxCount} older commits` }
}, Commands.ShowQuickFileHistory, [new GitUri(uri, last), undefined, log.maxCount, goBackCommand, undefined, npc]);
index++;
items.splice(0, 0, previousPageCommand);
} }
} }
const branch = await git.getBranch(uri.repoPath); const branch = await git.getBranch(uri.repoPath!);
const currentCommand = new CommandQuickPickItem({ const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`, label: `go back \u21A9`,
@@ -85,7 +86,7 @@ export class FileHistoryQuickPick {
if (!goBackCommand) { if (!goBackCommand) {
items.splice(index++, 0, new CommandQuickPickItem({ items.splice(index++, 0, new CommandQuickPickItem({
label: `$(history) Show Branch History`, label: `$(history) Show Branch History`,
description: `\u00a0 \u2014 \u00a0\u00a0 shows \u00a0$(git-branch) ${branch.name} history` description: `\u00a0 \u2014 \u00a0\u00a0 shows \u00a0$(git-branch) ${branch!.name} history`
}, Commands.ShowQuickCurrentBranchHistory, }, Commands.ShowQuickCurrentBranchHistory,
[ [
undefined, undefined,
@@ -93,9 +94,9 @@ export class FileHistoryQuickPick {
])); ]));
} }
const remotes = Arrays.uniqueBy(await git.getRemotes(uri.repoPath), _ => _.url, _ => !!_.provider); const remotes = Arrays.uniqueBy(await git.getRemotes(uri.repoPath!), _ => _.url, _ => !!_.provider);
if (remotes.length) { if (remotes.length) {
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, 'file', uri.getRelativePath(), branch.name, uri.sha, currentCommand)); items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, 'file', uri.getRelativePath(), branch!.name, uri.sha, currentCommand));
} }
if (goBackCommand) { if (goBackCommand) {

View File

@@ -12,8 +12,8 @@ export class OpenRemoteCommandQuickPickItem extends CommandQuickPickItem {
constructor(remote: GitRemote, type: RemoteOpenType, ...args: string[]) { constructor(remote: GitRemote, type: RemoteOpenType, ...args: string[]) {
super({ super({
label: `$(link-external) Open ${getNameFromRemoteOpenType(type)} in ${remote.provider.name}`, label: `$(link-external) Open ${getNameFromRemoteOpenType(type)} in ${remote.provider!.name}`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(repo) ${remote.provider.path}` description: `\u00a0 \u2014 \u00a0\u00a0 $(repo) ${remote.provider!.path}`
}, undefined, undefined); }, undefined, undefined);
this.remote = remote; this.remote = remote;
@@ -22,7 +22,7 @@ export class OpenRemoteCommandQuickPickItem extends CommandQuickPickItem {
} }
async execute(): Promise<{}> { async execute(): Promise<{}> {
return this.remote.provider.open(this.type, ...this.args); return this.remote.provider!.open(this.type, ...this.args!);
} }
} }
@@ -33,7 +33,7 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
constructor(remotes: GitRemote[], type: 'file', fileName: string, branch?: string, commit?: GitLogCommit, goBackCommand?: CommandQuickPickItem); constructor(remotes: GitRemote[], type: 'file', fileName: string, branch?: string, commit?: GitLogCommit, goBackCommand?: CommandQuickPickItem);
constructor(remotes: GitRemote[], type: 'file' | 'working-file', fileName: string, branch?: string, sha?: string, goBackCommand?: CommandQuickPickItem); constructor(remotes: GitRemote[], type: 'file' | 'working-file', fileName: string, branch?: string, sha?: string, goBackCommand?: CommandQuickPickItem);
constructor(remotes: GitRemote[], type: RemoteOpenType, branchOrShaOrFileName: string, goBackCommandOrFileBranch?: CommandQuickPickItem | string, fileShaOrCommit?: string | GitLogCommit, goBackCommand?: CommandQuickPickItem) { constructor(remotes: GitRemote[], type: RemoteOpenType, branchOrShaOrFileName: string, goBackCommandOrFileBranch?: CommandQuickPickItem | string, fileShaOrCommit?: string | GitLogCommit, goBackCommand?: CommandQuickPickItem) {
let fileBranch: string; let fileBranch: string | undefined = undefined;
if (typeof goBackCommandOrFileBranch === 'string') { if (typeof goBackCommandOrFileBranch === 'string') {
fileBranch = goBackCommandOrFileBranch; fileBranch = goBackCommandOrFileBranch;
} }
@@ -43,9 +43,9 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
const name = getNameFromRemoteOpenType(type); const name = getNameFromRemoteOpenType(type);
let fileSha: string; let fileSha: string | undefined = undefined;
let description: string; let description: string | undefined = undefined;
let placeHolder: string; let placeHolder: string | undefined = undefined;
switch (type) { switch (type) {
case 'branch': case 'branch':
description = `$(git-branch) ${branchOrShaOrFileName}`; description = `$(git-branch) ${branchOrShaOrFileName}`;
@@ -88,15 +88,15 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
const remote = remotes[0]; const remote = remotes[0];
if (remotes.length === 1) { if (remotes.length === 1) {
super({ super({
label: `$(link-external) Open ${name} in ${remote.provider.name}`, label: `$(link-external) Open ${name} in ${remote.provider!.name}`,
description: `\u00a0 \u2014 \u00a0\u00a0 $(repo) ${remote.provider.path} \u00a0\u2022\u00a0 ${description}` description: `\u00a0 \u2014 \u00a0\u00a0 $(repo) ${remote.provider!.path} \u00a0\u2022\u00a0 ${description}`
}, Commands.OpenInRemote, [undefined, remotes, type, [branchOrShaOrFileName, fileBranch, fileSha], goBackCommand]); }, Commands.OpenInRemote, [undefined, remotes, type, [branchOrShaOrFileName, fileBranch, fileSha], goBackCommand]);
return; return;
} }
const provider = remotes.every(_ => _.provider.name === remote.provider.name) const provider = remotes.every(_ => _.provider !== undefined && _.provider.name === remote.provider!.name)
? remote.provider.name ? remote.provider!.name
: 'Remote'; : 'Remote';
super({ super({

View File

@@ -11,13 +11,13 @@ export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPick
constructor(status: GitStatusFile, item?: QuickPickItem) { constructor(status: GitStatusFile, item?: QuickPickItem) {
const icon = status.getIcon(); const icon = status.getIcon();
let directory = GitService.normalizePath(path.dirname(status.fileName)); let directory: string | undefined = GitService.normalizePath(path.dirname(status.fileName));
if (!directory || directory === '.') { if (!directory || directory === '.') {
directory = undefined; directory = '';
} }
let description = (status.status === 'R' && status.originalFileName) let description = (status.status === 'R' && status.originalFileName)
? `${directory || ''} \u00a0\u2190\u00a0 ${status.originalFileName}` ? `${directory} \u00a0\u2190\u00a0 ${status.originalFileName}`
: directory; : directory;
super(status.Uri, item || { super(status.Uri, item || {
@@ -34,7 +34,7 @@ export class OpenStatusFilesCommandQuickPickItem extends CommandQuickPickItem {
super(item || { super(item || {
label: `$(file-symlink-file) Open Changed Files`, label: `$(file-symlink-file) Open Changed Files`,
description: undefined description: ''
//detail: `Opens all of the changed files in the repository` //detail: `Opens all of the changed files in the repository`
}, Commands.OpenChangedFiles, [undefined, uris]); }, Commands.OpenChangedFiles, [undefined, uris]);
} }
@@ -85,12 +85,12 @@ export class RepoStatusQuickPick {
items.splice(unstagedIndex, 0, new OpenStatusFilesCommandQuickPickItem(files.filter(_ => _.status !== 'D' && _.staged), { items.splice(unstagedIndex, 0, new OpenStatusFilesCommandQuickPickItem(files.filter(_ => _.status !== 'D' && _.staged), {
label: `\u00a0\u00a0\u00a0\u00a0 $(file-symlink-file) Open Staged Files`, label: `\u00a0\u00a0\u00a0\u00a0 $(file-symlink-file) Open Staged Files`,
description: undefined description: ''
})); }));
items.push(new OpenStatusFilesCommandQuickPickItem(files.filter(_ => _.status !== 'D' && !_.staged), { items.push(new OpenStatusFilesCommandQuickPickItem(files.filter(_ => _.status !== 'D' && !_.staged), {
label: `\u00a0\u00a0\u00a0\u00a0 $(file-symlink-file) Open Unstaged Files`, label: `\u00a0\u00a0\u00a0\u00a0 $(file-symlink-file) Open Unstaged Files`,
description: undefined description: ''
})); }));
} }
@@ -110,13 +110,13 @@ export class RepoStatusQuickPick {
items.push(new OpenStatusFilesCommandQuickPickItem(files.filter(_ => _.status !== 'D'))); items.push(new OpenStatusFilesCommandQuickPickItem(files.filter(_ => _.status !== 'D')));
items.push(new CommandQuickPickItem({ items.push(new CommandQuickPickItem({
label: '$(x) Close Unchanged Files', label: '$(x) Close Unchanged Files',
description: null description: ''
}, Commands.CloseUnchangedFiles)); }, Commands.CloseUnchangedFiles));
} }
else { else {
items.push(new CommandQuickPickItem({ items.push(new CommandQuickPickItem({
label: `No changes in the working tree`, label: `No changes in the working tree`,
description: null description: ''
}, Commands.ShowQuickRepoStatus, [undefined, goBackCommand])); }, Commands.ShowQuickRepoStatus, [undefined, goBackCommand]));
} }
@@ -150,7 +150,7 @@ export class RepoStatusQuickPick {
if (status.upstream && !status.state.ahead && !status.state.behind) { if (status.upstream && !status.state.ahead && !status.state.behind) {
items.splice(0, 0, new CommandQuickPickItem({ items.splice(0, 0, new CommandQuickPickItem({
label: `$(git-branch) ${status.branch} is up-to-date with \u00a0$(git-branch) ${status.upstream}`, label: `$(git-branch) ${status.branch} is up-to-date with \u00a0$(git-branch) ${status.upstream}`,
description: null description: ''
}, Commands.ShowQuickRepoStatus, [undefined, goBackCommand])); }, Commands.ShowQuickRepoStatus, [undefined, goBackCommand]));
} }

View File

@@ -29,7 +29,7 @@ export namespace Iterables {
} }
} }
export function find<T>(source: Iterable<T> | IterableIterator<T>, predicate: (item: T) => boolean): T { export function find<T>(source: Iterable<T> | IterableIterator<T>, predicate: (item: T) => boolean): T | null {
for (const item of source) { for (const item of source) {
if (predicate(item)) return item; if (predicate(item)) return item;
} }
@@ -76,8 +76,8 @@ export namespace Iterables {
return value; return value;
} }
export function last<T>(source: Iterable<T>): T { export function last<T>(source: Iterable<T>): T | null {
let item: T; let item: T | null = null;
for (item of source) { /* noop */ } for (item of source) { /* noop */ }
return item; return item;
} }

View File

@@ -29,7 +29,7 @@ class RenderWhitespaceConfiguration {
return this.value != null && this.value !== 'none'; return this.value != null && this.value !== 'none';
} }
get value(): string { get value(): string | undefined {
return this.inspection.workspaceValue || this.inspection.globalValue || this.inspection.defaultValue; return this.inspection.workspaceValue || this.inspection.globalValue || this.inspection.defaultValue;
} }
@@ -97,7 +97,7 @@ export class WhitespaceController extends Disposable {
private _onConfigurationChanged() { private _onConfigurationChanged() {
if (this._disposed) return; if (this._disposed) return;
const inspection = workspace.getConfiguration('editor').inspect<string>('renderWhitespace'); const inspection = workspace.getConfiguration('editor').inspect<string>('renderWhitespace')!;
if (!this._count) { if (!this._count) {
this._configuration = new RenderWhitespaceConfiguration(inspection); this._configuration = new RenderWhitespaceConfiguration(inspection);
@@ -125,8 +125,8 @@ export class WhitespaceController extends Disposable {
private async _overrideWhitespace() { private async _overrideWhitespace() {
Logger.log(`Override whitespace`); Logger.log(`Override whitespace`);
const config = workspace.getConfiguration('editor'); const cfg = workspace.getConfiguration('editor');
return config.update('renderWhitespace', 'none', this._configuration.location === SettingLocation.global); return cfg.update('renderWhitespace', 'none', this._configuration.location === SettingLocation.global);
} }
async restore() { async restore() {
@@ -142,8 +142,8 @@ export class WhitespaceController extends Disposable {
private async _restoreWhitespace() { private async _restoreWhitespace() {
Logger.log(`Restore whitespace`); Logger.log(`Restore whitespace`);
const config = workspace.getConfiguration('editor'); const cfg = workspace.getConfiguration('editor');
return config.update('renderWhitespace', return cfg.update('renderWhitespace',
this._configuration.location === SettingLocation.default this._configuration.location === SettingLocation.default
? undefined ? undefined
: this._configuration.value, : this._configuration.value,

View File

@@ -1,17 +1,17 @@
{ {
"compilerOptions": { "compilerOptions": {
"forceConsistentCasingInFileNames": true,
"lib": [ "es6" ], "lib": [ "es6" ],
"module": "commonjs", "module": "commonjs",
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true, "noImplicitReturns": true,
"noImplicitThis": false,
"noUnusedLocals": true, "noUnusedLocals": true,
"outDir": "out", "outDir": "out",
"removeComments": true, "removeComments": true,
"rootDir": ".", "rootDir": ".",
"skipLibCheck": true,
"sourceMap": true, "sourceMap": true,
"strictNullChecks": false, "strict": true,
"target": "es6" "target": "es6"
}, },
"exclude": [ "exclude": [