Browse Source

Enables typescript strict mode

Fixes all the compile/lint issues
main
Eric Amodio 7 years ago
parent
commit
ee29596d45
52 changed files with 525 additions and 461 deletions
  1. +3
    -3
      src/activeEditorTracker.ts
  2. +35
    -33
      src/blameActiveLineController.ts
  3. +6
    -6
      src/blameAnnotationController.ts
  4. +13
    -7
      src/blameAnnotationProvider.ts
  5. +5
    -5
      src/commands/closeUnchangedFiles.ts
  6. +1
    -2
      src/commands/common.ts
  7. +1
    -0
      src/commands/diffDirectory.ts
  8. +3
    -3
      src/commands/diffLineWithPrevious.ts
  9. +1
    -1
      src/commands/diffLineWithWorking.ts
  10. +2
    -1
      src/commands/diffWithBranch.ts
  11. +2
    -2
      src/commands/diffWithNext.ts
  12. +2
    -2
      src/commands/diffWithPrevious.ts
  13. +2
    -1
      src/commands/diffWithWorking.ts
  14. +2
    -2
      src/commands/keyboard.ts
  15. +1
    -1
      src/commands/openCommitInRemote.ts
  16. +1
    -1
      src/commands/openFileInRemote.ts
  17. +4
    -3
      src/commands/openInRemote.ts
  18. +9
    -3
      src/commands/showCommitSearch.ts
  19. +6
    -6
      src/commands/showQuickBranchHistory.ts
  20. +9
    -7
      src/commands/showQuickCommitDetails.ts
  21. +7
    -5
      src/commands/showQuickCommitFileDetails.ts
  22. +2
    -1
      src/commands/showQuickStashList.ts
  23. +9
    -9
      src/comparers.ts
  24. +1
    -1
      src/configuration.ts
  25. +4
    -4
      src/extension.ts
  26. +8
    -8
      src/git/git.ts
  27. +11
    -11
      src/git/gitContextTracker.ts
  28. +2
    -2
      src/git/gitUri.ts
  29. +1
    -1
      src/git/models/commit.ts
  30. +65
    -65
      src/git/models/logCommit.ts
  31. +1
    -1
      src/git/models/stashCommit.ts
  32. +27
    -18
      src/git/parsers/blameParser.ts
  33. +42
    -29
      src/git/parsers/logParser.ts
  34. +16
    -14
      src/git/parsers/stashParser.ts
  35. +9
    -7
      src/git/parsers/statusParser.ts
  36. +3
    -1
      src/git/remotes/factory.ts
  37. +5
    -3
      src/git/remotes/provider.ts
  38. +24
    -24
      src/gitCodeLensProvider.ts
  39. +2
    -2
      src/gitContentProvider.ts
  40. +85
    -83
      src/gitService.ts
  41. +1
    -0
      src/logger.ts
  42. +9
    -8
      src/quickPicks/branchHistory.ts
  43. +1
    -1
      src/quickPicks/branches.ts
  44. +11
    -11
      src/quickPicks/commitDetails.ts
  45. +13
    -9
      src/quickPicks/commitFileDetails.ts
  46. +13
    -10
      src/quickPicks/common.ts
  47. +13
    -12
      src/quickPicks/fileHistory.ts
  48. +11
    -11
      src/quickPicks/remotes.ts
  49. +9
    -9
      src/quickPicks/repoStatus.ts
  50. +3
    -3
      src/system/iterable.ts
  51. +6
    -6
      src/whitespaceController.ts
  52. +3
    -3
      tsconfig.json

+ 3
- 3
src/activeEditorTracker.ts View File

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

+ 35
- 33
src/blameActiveLineController.ts View File

@ -54,38 +54,38 @@ export class BlameActiveLineController extends Disposable {
}
private _onConfigurationChanged() {
const config = workspace.getConfiguration().get<IConfig>(ExtensionKey);
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
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;
if (config.statusBar.enabled) {
if (cfg.statusBar.enabled) {
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 = 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;
if (config.blame.annotation.activeLine !== 'off' && this._editor) {
if (cfg.blame.annotation.activeLine !== 'off' && this._editor) {
this._editor.setDecorations(activeLineDecoration, []);
}
}
if (!Objects.areEquivalent(config.blame.annotation.activeLineDarkColor, this._config && this._config.blame.annotation.activeLineDarkColor) ||
!Objects.areEquivalent(config.blame.annotation.activeLineLightColor, this._config && this._config.blame.annotation.activeLineLightColor)) {
if (!Objects.areEquivalent(cfg.blame.annotation.activeLineDarkColor, this._config && this._config.blame.annotation.activeLineDarkColor) ||
!Objects.areEquivalent(cfg.blame.annotation.activeLineLightColor, this._config && this._config.blame.annotation.activeLineLightColor)) {
changed = true;
}
this._config = config;
this._config = cfg;
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) {
const subscriptions: Disposable[] = [];
@ -103,8 +103,8 @@ export class BlameActiveLineController extends Disposable {
this._onActiveTextEditorChanged(window.activeTextEditor);
}
private isEditorBlameable(editor: TextEditor): boolean {
if (!editor || !editor.document) return false;
private isEditorBlameable(editor: TextEditor | undefined): boolean {
if (editor === undefined || editor.document === undefined) return false;
const scheme = editor.document.uri.scheme;
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);
}
private async _onActiveTextEditorChanged(editor: TextEditor) {
private async _onActiveTextEditorChanged(editor: TextEditor | undefined) {
this._currentLine = -1;
const previousEditor = this._editor;
previousEditor && previousEditor.setDecorations(activeLineDecoration, []);
if (!this.isEditorBlameable(editor)) {
if (editor === undefined || !this.isEditorBlameable(editor)) {
this.clear(editor);
this._editor = undefined;
@ -128,7 +128,7 @@ export class BlameActiveLineController extends Disposable {
return;
}
this._blameable = editor && editor.document && !editor.document.isDirty;
this._blameable = editor !== undefined && editor.document !== undefined && !editor.document.isDirty;
this._editor = editor;
this._uri = await GitUri.fromUri(editor.document.uri, this.git);
const maxLines = this._config.advanced.caching.statusBar.maxLines;
@ -183,29 +183,29 @@ export class BlameActiveLineController extends Disposable {
private async _updateBlame(line: number, editor: TextEditor) {
line = line - this._uri.offset;
let commit: GitCommit;
let commitLine: IGitCommitLine;
let commit: GitCommit | undefined = undefined;
let commitLine: IGitCommitLine | undefined = undefined;
// Since blame information isn't valid when there are unsaved changes -- don't show any status
if (this._blameable && line >= 0) {
if (this._useCaching) {
const blame = this._blame && await this._blame;
if (!blame || !blame.lines.length) {
if (blame === undefined || !blame.lines.length) {
this.clear(editor);
return;
}
commitLine = blame.lines[line];
const sha = commitLine && commitLine.sha;
commit = sha && blame.commits.get(sha);
const sha = commitLine === undefined ? undefined : commitLine.sha;
commit = sha === undefined ? undefined : blame.commits.get(sha);
}
else {
const blameLine = await this.git.getBlameForLine(this._uri, line);
commitLine = blameLine && blameLine.line;
commit = blameLine && blameLine.commit;
commitLine = blameLine === undefined ? undefined : blameLine.line;
commit = blameLine === undefined ? undefined : blameLine.commit;
}
}
if (commit) {
if (commit !== undefined && commitLine !== undefined) {
this.show(commit, commitLine, editor);
}
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, []);
// 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) {
@ -227,7 +227,7 @@ export class BlameActiveLineController extends Disposable {
// I have no idea why I need this protection -- but it happens
if (!editor.document) return;
if (this._config.statusBar.enabled) {
if (this._config.statusBar.enabled && this._statusBarItem !== undefined) {
switch (this._config.statusBar.date) {
case 'off':
this._statusBarItem.text = `$(git-commit) ${commit.author}`;
@ -284,7 +284,7 @@ export class BlameActiveLineController extends Disposable {
const activeLine = this._config.blame.annotation.activeLine;
const offset = this._uri.offset;
const config = {
const cfg = {
annotation: {
sha: true,
author: this._config.statusBar.enabled ? false : this._config.blame.annotation.author,
@ -293,10 +293,10 @@ export class BlameActiveLineController extends Disposable {
}
} 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
let logCommit: GitCommit;
let logCommit: GitCommit | undefined = undefined;
if (!commit.isUncommitted) {
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
if (!editor.document) return;
let hoverMessage: string | string[];
let hoverMessage: string | string[] | undefined = undefined;
if (activeLine !== 'inline') {
// 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;
// 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))) {
hoverMessage = BlameAnnotationFormatter.getAnnotationHover(config, blameLine, logCommit || commit);
hoverMessage = BlameAnnotationFormatter.getAnnotationHover(cfg, blameLine, logCommit || commit);
}
}
let decorationOptions: DecorationOptions;
let decorationOptions: DecorationOptions | undefined = undefined;
switch (activeLine) {
case 'both':
case 'inline':
@ -347,7 +347,9 @@ export class BlameActiveLineController extends Disposable {
break;
}
decorationOptions && editor.setDecorations(activeLineDecoration, [decorationOptions]);
if (decorationOptions !== undefined) {
editor.setDecorations(activeLineDecoration, [decorationOptions]);
}
}
}
}

+ 6
- 6
src/blameAnnotationController.ts View File

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

+ 13
- 7
src/blameAnnotationProvider.ts View File

@ -24,7 +24,7 @@ export class BlameAnnotationProvider extends Disposable {
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[] = [];
@ -39,8 +39,12 @@ export class BlameAnnotationProvider extends Disposable {
this.editor.setDecorations(BlameDecorations.annotation, []);
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
if (BlameDecorations.highlight) {
setTimeout(() => this.editor.setDecorations(BlameDecorations.highlight, []), 1);
if (BlameDecorations.highlight !== undefined) {
setTimeout(() => {
if (BlameDecorations.highlight === undefined) return;
this.editor.setDecorations(BlameDecorations.highlight, []);
}, 1);
}
}
catch (ex) { }
@ -64,7 +68,7 @@ export class BlameAnnotationProvider extends Disposable {
}
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)
if (this._config.annotation.style !== BlameAnnotationStyle.Trailing) {
whitespacePromise = this.whitespaceController && this.whitespaceController.override();
@ -116,7 +120,7 @@ export class BlameAnnotationProvider extends Disposable {
const offset = this.uri.offset;
let sha: string;
let sha: string | undefined = undefined;
if (typeof shaOrLine === 'string') {
sha = shaOrLine;
}
@ -149,7 +153,8 @@ export class BlameAnnotationProvider extends Disposable {
let count = 0;
let lastSha: string;
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;
if (commit.isUncommitted) {
@ -248,7 +253,8 @@ export class BlameAnnotationProvider extends Disposable {
}
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;
if (commit.isUncommitted) {

+ 5
- 5
src/commands/closeUnchangedFiles.ts View File

@ -33,11 +33,11 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
let active = window.activeTextEditor;
let editor = active;
do {
if (editor) {
if ((editor.document && editor.document.isDirty) ||
uris.some(_ => UriComparer.equals(_, editor.document && editor.document.uri))) {
if (editor !== undefined) {
if ((editor.document !== undefined && editor.document.isDirty) ||
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 (!active) {
if (active === undefined) {
active = editor;
}
editor = await editorTracker.awaitNext(500);
@ -55,7 +55,7 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand {
}
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();

+ 1
- 2
src/commands/common.ts View File

@ -59,7 +59,6 @@ export const CommandContext = {
Key: 'gitlens:key' as CommandContext
};
export function setCommandContext(key: CommandContext | string, value: any) {
return commands.executeCommand(BuiltInCommands.SetContext, key, value);
}
@ -119,7 +118,7 @@ export abstract class ActiveEditorCommand extends Command {
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() {
return lastCommand;
}

+ 1
- 0
src/commands/diffDirectory.ts View File

@ -32,6 +32,7 @@ export class DiffDirectoryCommand extends ActiveEditorCommand {
if (!shaOrBranch1) {
const branches = await this.git.getBranches(repoPath);
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`);
if (!pick) return undefined;

+ 3
- 3
src/commands/diffLineWithPrevious.ts 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 (commit.isUncommitted) {
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;
return commands.executeCommand(Commands.DiffWithWorking, uri, commit, line);
}
@ -56,10 +56,10 @@ export class DiffLineWithPreviousCommand extends ActiveEditorCommand {
try {
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)
]);
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
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
}

+ 1
- 1
src/commands/diffLineWithWorking.ts View File

@ -34,7 +34,7 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
commit = blame.commit;
// If the line is uncommitted, find the previous commit
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;
}
}

+ 2
- 1
src/commands/diffWithBranch.ts View File

@ -22,6 +22,7 @@ export class DiffWithBranchCommand extends ActiveEditorCommand {
const line = (editor && editor.selection.active.line) || 0;
const gitUri = await GitUri.fromUri(uri, this.git);
if (gitUri.repoPath === undefined) return undefined;
const branches = await this.git.getBranches(gitUri.repoPath);
const pick = await BranchesQuickPick.show(branches, `Compare ${path.basename(gitUri.fsPath)} to \u2026`, goBackCommand);
@ -36,7 +37,7 @@ export class DiffWithBranchCommand extends ActiveEditorCommand {
try {
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' });
}
catch (ex) {

+ 2
- 2
src/commands/diffWithNext.ts View File

@ -42,7 +42,7 @@ export class DiffWithNextCommand extends ActiveEditorCommand {
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`);
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.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' });
}
catch (ex) {

+ 2
- 2
src/commands/diffWithPrevious.ts View File

@ -43,7 +43,7 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
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`);
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.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
return await commands.executeCommand(BuiltInCommands.RevealLine, { lineNumber: line, at: 'center' });
}

+ 2
- 1
src/commands/diffWithWorking.ts View File

@ -39,10 +39,11 @@ export class DiffWithWorkingCommand extends ActiveEditorCommand {
const gitUri = await GitUri.fromUri(uri, this.git);
const workingFileName = await this.git.findWorkingFileName(gitUri.repoPath, gitUri.fsPath);
if (workingFileName === undefined) return undefined;
try {
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' });
}
catch (ex) {

+ 2
- 2
src/commands/keyboard.ts 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 _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();
}
async execute(key: Keys): Promise<{}> {
async execute(key: Keys): Promise<{} | undefined> {
if (!mappings.length) return undefined;
try {

+ 1
- 1
src/commands/openCommitInRemote.ts View File

@ -34,7 +34,7 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
let commit = blame.commit;
// If the line is uncommitted, find the previous commit
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);

+ 1
- 1
src/commands/openFileInRemote.ts View File

@ -27,7 +27,7 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
try {
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 }));
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) {
Logger.error(ex, 'OpenFileInRemoteCommand');

+ 4
- 3
src/commands/openInRemote.ts View File

@ -11,20 +11,21 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
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)) {
uri = editor && editor.document && editor.document.uri;
}
try {
if (!remotes) return undefined;
if (remotes === undefined) return undefined;
if (type === undefined) throw new Error(`Invalid type ${type}`);
if (remotes.length === 1) {
const command = new OpenRemoteCommandQuickPickItem(remotes[0], type, ...args);
return command.execute();
}
let placeHolder: string;
let placeHolder: string = '';
switch (type) {
case 'branch':
placeHolder = `open ${args[0]} branch in\u2026`;

+ 9
- 3
src/commands/showCommitSearch.ts View File

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

+ 6
- 6
src/commands/showQuickBranchHistory.ts View File

@ -22,12 +22,12 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
maxCount = this.git.config.advanced.maxQuickHistory;
}
let progressCancellation = branch && BranchHistoryQuickPick.showProgress(branch);
let progressCancellation = branch === undefined ? undefined : BranchHistoryQuickPick.showProgress(branch);
try {
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 pick = await BranchesQuickPick.show(branches, `Show history for branch\u2026`);
@ -38,7 +38,7 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
}
branch = pick.branch.name;
if (!branch) return undefined;
if (branch === undefined) return undefined;
progressCancellation = BranchHistoryQuickPick.showProgress(branch);
}
@ -48,9 +48,9 @@ export class ShowQuickBranchHistoryCommand extends ActiveEditorCachedCommand {
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 instanceof CommandQuickPickItem) {

+ 9
- 7
src/commands/showQuickCommitDetails.ts View File

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

+ 7
- 5
src/commands/showQuickCommitFileDetails.ts View File

@ -46,24 +46,26 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
try {
if (!commit || (commit.type !== 'file' && commit.type !== 'stash')) {
if (fileLog) {
commit = fileLog.commits.get(sha);
commit = fileLog.commits.get(sha!);
// If we can't find the commit, kill the fileLog
if (!commit) {
if (commit === undefined) {
fileLog = undefined;
}
}
if (!fileLog) {
if (fileLog === undefined) {
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
commit.workingFileName = workingFileName;
commit.workingFileName = await this.git.findWorkingFileName(commit);
const shortSha = sha.substring(0, 8);
const shortSha = sha!.substring(0, 8);
if (!goBackCommand) {
// Create a command to get back to the commit details

+ 2
- 1
src/commands/showQuickStashList.ts View File

@ -18,9 +18,10 @@ export class ShowQuickStashListCommand extends ActiveEditorCachedCommand {
try {
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);
if (stash === undefined) return window.showWarningMessage(`Unable to show stashed changes`);
// Create a command to get back to here
const currentCommand = new CommandQuickPickItem({

+ 9
- 9
src/comparers.ts View File

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

+ 1
- 1
src/configuration.ts View File

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

+ 4
- 4
src/extension.ts View File

@ -30,14 +30,14 @@ export async function activate(context: ExtensionContext) {
Logger.configure(context);
Telemetry.configure(ApplicationInsightsKey);
const gitlens = extensions.getExtension(QualifiedExtensionId);
const gitlens = extensions.getExtension(QualifiedExtensionId)!;
const gitlensVersion = gitlens.packageJSON.version;
const rootPath = workspace.rootPath && workspace.rootPath.replace(/\\/g, '/');
Logger.log(`GitLens(v${gitlensVersion}) active: ${rootPath}`);
const config = workspace.getConfiguration().get<IConfig>(ExtensionKey);
const gitPath = config.advanced.git;
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
const gitPath = cfg.advanced.git;
try {
await GitService.getGitPath(gitPath);
@ -114,7 +114,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new StashSaveCommand(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

+ 8
- 8
src/git/git.ts View File

@ -54,8 +54,8 @@ export class Git {
return git;
}
static async getRepoPath(cwd: string) {
if (!cwd) return '';
static async getRepoPath(cwd: string | undefined) {
if (cwd === undefined) return '';
const data = await gitCommand(cwd, 'rev-parse', '--show-toplevel');
if (!data) return '';
@ -63,7 +63,7 @@ export class Git {
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 suffix = Git.isSha(branchOrSha) ? branchOrSha.substring(0, 8) : branchOrSha;
@ -112,7 +112,7 @@ export class Git {
}
}
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);
}
@ -126,7 +126,7 @@ export class Git {
// 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 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 (!sha || maxCount > 2) {
if (!sha || maxCount! > 2) {
params.push(`--no-merges`);
}
else {
@ -236,7 +236,7 @@ export class Git {
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`];
if (maxCount) {
params.push(`-n${maxCount}`);
@ -262,7 +262,7 @@ export class Git {
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);
branchOrSha = branchOrSha.replace('^', '');

+ 11
- 11
src/git/gitContextTracker.ts View File

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

+ 2
- 2
src/git/gitUri.ts View File

@ -43,7 +43,7 @@ export class GitUri extends Uri {
const commit = commitOrRepoPath;
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.repoPath = commit.repoPath;
}
@ -72,7 +72,7 @@ export class GitUri extends Uri {
}
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) {

+ 1
- 1
src/git/models/commit.ts View File

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

+ 65
- 65
src/git/models/logCommit.ts View File

@ -1,66 +1,66 @@
'use strict';
import { Uri } from 'vscode';
import { GitCommit, GitCommitType, IGitCommitLine } from './commit';
import { IGitStatusFile, GitStatusFileStatus } from './status';
import * as path from 'path';
export class GitLogCommit extends GitCommit {
fileNames: string;
fileStatuses: IGitStatusFile[];
nextSha?: string;
nextFileName?: string;
parentShas: string[];
status?: GitStatusFileStatus;
constructor(
type: GitCommitType,
repoPath: string,
sha: string,
fileName: string,
author: string,
date: Date,
message: string,
status?: GitStatusFileStatus,
fileStatuses?: IGitStatusFile[],
lines?: IGitCommitLine[],
originalFileName?: string,
previousSha?: string,
previousFileName?: string
) {
super(type, repoPath, sha, fileName, author, date, message, lines, originalFileName, previousSha, previousFileName);
this.fileNames = this.fileName;
if (fileStatuses) {
this.fileStatuses = fileStatuses.filter(_ => !!_.fileName);
const fileStatus = this.fileStatuses[0];
this.fileName = fileStatus.fileName;
this.status = fileStatus.status;
}
else {
this.fileStatuses = [{ status: status, fileName: fileName, originalFileName: originalFileName }];
this.status = status;
}
}
get isMerge() {
return this.parentShas && this.parentShas.length > 1;
}
get nextShortSha() {
return this.nextSha && this.nextSha.substring(0, 8);
}
get nextUri(): Uri {
return this.nextFileName ? Uri.file(path.resolve(this.repoPath, this.nextFileName)) : this.uri;
}
getDiffStatus(): string {
const added = this.fileStatuses.filter(_ => _.status === 'A' || _.status === '?').length;
const deleted = this.fileStatuses.filter(_ => _.status === 'D').length;
const changed = this.fileStatuses.filter(_ => _.status !== 'A' && _.status !== '?' && _.status !== 'D').length;
return `+${added} ~${changed} -${deleted}`;
}
'use strict';
import { Uri } from 'vscode';
import { GitCommit, GitCommitType, IGitCommitLine } from './commit';
import { IGitStatusFile, GitStatusFileStatus } from './status';
import * as path from 'path';
export class GitLogCommit extends GitCommit {
fileNames: string;
fileStatuses: IGitStatusFile[];
nextSha?: string;
nextFileName?: string;
parentShas: string[];
status?: GitStatusFileStatus;
constructor(
type: GitCommitType,
repoPath: string,
sha: string,
fileName: string,
author: string,
date: Date,
message: string,
status?: GitStatusFileStatus,
fileStatuses?: IGitStatusFile[],
lines?: IGitCommitLine[],
originalFileName?: string,
previousSha?: string,
previousFileName?: string
) {
super(type, repoPath, sha, fileName, author, date, message, lines, originalFileName, previousSha, previousFileName);
this.fileNames = this.fileName;
if (fileStatuses) {
this.fileStatuses = fileStatuses.filter(_ => !!_.fileName);
const fileStatus = this.fileStatuses[0];
this.fileName = fileStatus.fileName;
this.status = fileStatus.status;
}
else {
this.fileStatuses = [{ status: status, fileName: fileName, originalFileName: originalFileName } as IGitStatusFile];
this.status = status;
}
}
get isMerge() {
return this.parentShas && this.parentShas.length > 1;
}
get nextShortSha() {
return this.nextSha && this.nextSha.substring(0, 8);
}
get nextUri(): Uri {
return this.nextFileName ? Uri.file(path.resolve(this.repoPath, this.nextFileName)) : this.uri;
}
getDiffStatus(): string {
const added = this.fileStatuses.filter(_ => _.status === 'A' || _.status === '?').length;
const deleted = this.fileStatuses.filter(_ => _.status === 'D').length;
const changed = this.fileStatuses.filter(_ => _.status !== 'A' && _.status !== '?' && _.status !== 'D').length;
return `+${added} ~${changed} -${deleted}`;
}
}

+ 1
- 1
src/git/models/stashCommit.ts View File

@ -19,7 +19,7 @@ export class GitStashCommit extends GitLogCommit {
previousSha?: 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() {

+ 27
- 18
src/git/parsers/blameParser.ts View File

@ -10,7 +10,7 @@ interface IBlameEntry {
originalLine: number;
lineCount: number;
author?: string;
author: string;
// authorEmail?: string;
authorDate?: string;
authorTimeZone?: string;
@ -30,7 +30,7 @@ interface IBlameEntry {
export class GitBlameParser {
private static _parseEntries(data: string): IBlameEntry[] {
private static _parseEntries(data: string): IBlameEntry[] | undefined {
if (!data) return undefined;
const lines = data.split('\n');
@ -38,7 +38,7 @@ export class GitBlameParser {
const entries: IBlameEntry[] = [];
let entry: IBlameEntry;
let entry: IBlameEntry | undefined = undefined;
let position = -1;
while (++position < lines.length) {
let lineParts = lines[position].split(' ');
@ -46,13 +46,13 @@ export class GitBlameParser {
continue;
}
if (!entry) {
if (entry === undefined) {
entry = {
sha: lineParts[0],
originalLine: parseInt(lineParts[1], 10) - 1,
line: parseInt(lineParts[2], 10) - 1,
lineCount: parseInt(lineParts[3], 10)
};
} as IBlameEntry;
continue;
}
@ -116,7 +116,7 @@ export class GitBlameParser {
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);
if (!entries) return undefined;
@ -129,24 +129,26 @@ export class GitBlameParser {
for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i];
if (i === 0 && !repoPath) {
if (i === 0 && repoPath === undefined) {
// 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));
}
let commit = commits.get(entry.sha);
if (!commit) {
let author = authors.get(entry.author);
if (!author) {
author = {
name: entry.author,
lineCount: 0
};
authors.set(entry.author, author);
if (commit === undefined) {
if (entry.author !== undefined) {
let author = authors.get(entry.author);
if (author === undefined) {
author = {
name: entry.author,
lineCount: 0
};
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) {
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 values =

+ 42
- 29
src/git/parsers/logParser.ts View File

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

+ 16
- 14
src/git/parsers/stashParser.ts View File

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

+ 9
- 7
src/git/parsers/statusParser.ts View File

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

+ 3
- 1
src/git/remotes/factory.ts View File

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

+ 5
- 3
src/git/remotes/provider.ts View File

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

+ 24
- 24
src/gitCodeLensProvider.ts View File

@ -1,6 +1,6 @@
'use strict';
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 { BuiltInCommands, DocumentSchemes, ExtensionKey } from './constants';
import { CodeLensCommand, CodeLensLocation, IConfig, ICodeLensLanguageLocation } from './configuration';
@ -10,22 +10,22 @@ import * as moment from 'moment';
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);
}
getBlame(): IGitBlameLines {
getBlame(): IGitBlameLines | undefined {
return this.blame();
}
}
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);
}
getBlame(): IGitBlameLines {
getBlame(): IGitBlameLines | undefined {
return this.blame();
}
}
@ -43,11 +43,11 @@ export class GitCodeLensProvider implements CodeLensProvider {
private _documentIsDirty: boolean;
constructor(context: ExtensionContext, private git: GitService) {
this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey);
this._config = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
}
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');
this._onDidChangeCodeLenses.fire();
@ -56,7 +56,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
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) {
languageLocations = {
language: undefined,
@ -72,10 +72,10 @@ export class GitCodeLensProvider implements CodeLensProvider {
const gitUri = await GitUri.fromUri(document.uri, this.git);
const blamePromise = this.git.getBlameForFile(gitUri);
let blame: IGitBlame;
let blame: IGitBlame | undefined;
if (languageLocations.location === CodeLensLocation.Document) {
blame = await blamePromise;
if (!blame || !blame.lines.length) return lenses;
if (blame === undefined || !blame.lines.length) return lenses;
}
else {
const values = await Promise.all([
@ -84,25 +84,25 @@ export class GitCodeLensProvider implements CodeLensProvider {
]);
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[];
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')) {
// 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)) {
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) {
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)));
}
if (this._config.codeLens.authors.enabled) {
if (!blameForRangeFn) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange));
if (blameForRangeFn === undefined) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame!, gitUri, blameRange));
}
if (!this._documentIsDirty) {
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);
}
let blameForRangeFn: () => IGitBlameLines;
let blameForRangeFn: (() => IGitBlameLines | undefined) | undefined = undefined;
if (this._documentIsDirty || this._config.codeLens.recentChange.enabled) {
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))));
@ -220,7 +220,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
}
if (multiline) {
if (!blameForRangeFn) {
if (blameForRangeFn === undefined) {
blameForRangeFn = Functions.once(() => this.git.getBlameForRangeSync(blame, gitUri, blameRange));
}
if (!this._documentIsDirty) {
@ -250,14 +250,12 @@ export class GitCodeLensProvider implements CodeLensProvider {
title = 'Cannot determine authors (unsaved changes)';
}
lens.command = {
title: title,
command: undefined
};
lens.command = { title: title } as Command;
return lens;
}
const blame = lens.getBlame();
if (blame === undefined) return lens;
const recentCommit = Iterables.first(blame.commits.values());
title = `${recentCommit.author}, ${moment(recentCommit.date).fromNow()}`;
@ -280,6 +278,8 @@ export class GitCodeLensProvider implements CodeLensProvider {
_resolveGitAuthorsCodeLens(lens: GitAuthorsCodeLens, token: CancellationToken): CodeLens {
const blame = lens.getBlame();
if (blame === undefined) return lens;
const count = blame.authors.size;
let title = `${count} ${count > 1 ? 'authors' : 'author'} (${Iterables.first(blame.authors.values()).name}${count > 1 ? ' and others' : ''})`;
if (this._config.codeLens.debug) {
@ -366,7 +366,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = {
title: title,
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;
}
@ -375,7 +375,7 @@ export class GitCodeLensProvider implements CodeLensProvider {
lens.command = {
title: title,
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;
}

+ 2
- 2
src/gitContentProvider.ts View File

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

+ 85
- 83
src/gitService.ts View File

@ -15,7 +15,7 @@ import * as path from 'path';
export { GitUri };
export * from './git/models/models';
export { getNameFromRemoteOpenType, RemoteOpenType } from './git/remotes/provider';
export { getNameFromRemoteOpenType, RemoteOpenType, RemoteProvider } from './git/remotes/provider';
export * from './git/gitContextTracker';
class UriCacheEntry {
@ -28,8 +28,9 @@ class GitCacheEntry {
blame?: ICachedBlame;
log?: ICachedLog;
get hasErrors() {
return !!((this.blame && this.blame.errorMessage) || (this.log && this.log.errorMessage));
get hasErrors(): boolean {
return (this.blame !== undefined && this.blame.errorMessage !== undefined) ||
(this.log !== undefined && this.log.errorMessage !== undefined);
}
constructor(public key: string) { }
@ -69,23 +70,25 @@ export class GitService extends Disposable {
return this._onDidBlameFail.event;
}
private _gitCache: Map<string, GitCacheEntry> | undefined;
private _gitCache: Map<string, GitCacheEntry>;
private _remotesCache: Map<string, GitRemote[]>;
private _cacheDisposable: Disposable | undefined;
private _uriCache: Map<string, UriCacheEntry> | undefined;
private _uriCache: Map<string, UriCacheEntry>;
config: IConfig;
private _codeLensProvider: GitCodeLensProvider | undefined;
private _codeLensProviderDisposable: Disposable | undefined;
private _disposable: Disposable;
private _fsWatcher: FileSystemWatcher;
private _disposable: Disposable | undefined;
private _fsWatcher: FileSystemWatcher | undefined;
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) {
super(() => this.dispose());
this._gitCache = new Map();
this._remotesCache = new Map();
this._uriCache = new Map();
this._onConfigurationChanged();
@ -110,27 +113,24 @@ export class GitService extends Disposable {
this._fsWatcher && this._fsWatcher.dispose();
this._fsWatcher = undefined;
this._gitCache && this._gitCache.clear();
this._gitCache = undefined;
this._remotesCache && this._remotesCache.clear();
this._remotesCache = undefined;
this._uriCache && this._uriCache.clear();
this._uriCache = undefined;
this._gitCache.clear();
this._remotesCache.clear();
this._uriCache.clear();
}
public get UseGitCaching() {
return !!this._gitCache;
public get UseCaching() {
return this.config.advanced.caching.enabled;
}
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 advancedChanged = !Objects.areEquivalent(config.advanced, this.config && this.config.advanced);
const codeLensChanged = !Objects.areEquivalent(cfg.codeLens, this.config && this.config.codeLens);
const advancedChanged = !Objects.areEquivalent(cfg.advanced, this.config && this.config.advanced);
if (codeLensChanged) {
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) {
this._codeLensProvider.reset();
}
@ -145,14 +145,11 @@ export class GitService extends Disposable {
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 (config.advanced.caching.enabled) {
this._gitCache = new Map();
this._remotesCache = new Map();
if (cfg.advanced.caching.enabled) {
this._cacheDisposable && this._cacheDisposable.dispose();
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 = undefined;
this._gitCache && this._gitCache.clear();
this._gitCache = undefined;
this._remotesCache && this._remotesCache.clear();
this._remotesCache = undefined;
this._gitCache.clear();
this._remotesCache.clear();
}
this._gitignore = new Promise<ignore.Ignore | undefined>((resolve, reject) => {
if (!config.advanced.gitignore.enabled) {
if (!cfg.advanced.gitignore.enabled) {
resolve(undefined);
return;
}
@ -202,18 +196,18 @@ export class GitService extends Disposable {
});
}
this.config = config;
this.config = cfg;
}
private _onGitChanged() {
this._gitCache && this._gitCache.clear();
this._gitCache.clear();
this._onDidChangeGitCache.fire();
this._codeLensProvider && this._codeLensProvider.reset();
}
private _removeCachedEntry(document: TextDocument, reason: RemoveCacheReason) {
if (!this.UseGitCaching) return;
if (!this.UseCaching) return;
if (document.uri.scheme !== DocumentSchemes.File) return;
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)));
}
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 commit = log && Iterables.first(log.commits.values());
if (commit) return commit;
fileName = await this.findNextFileName(repoPath, fileName, sha);
if (fileName) {
log = await this.getLogForFile(repoPath, fileName, sha, 1, undefined, true);
const nextFileName = await this.findNextFileName(repoPath, fileName, sha);
if (nextFileName) {
log = await this.getLogForFile(repoPath, nextFileName, sha, 1, undefined, true);
commit = log && Iterables.first(log.commits.values());
}
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);
return (await this._fileExists(repoPath, fileName))
@ -262,7 +256,7 @@ export class GitService extends Disposable {
: 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) {
// Get the most recent commit for this file name
const c = await this.getLogCommit(repoPath, fileName);
@ -282,12 +276,14 @@ export class GitService extends Disposable {
return status.fileName;
}
async findWorkingFileName(commit: GitCommit): Promise<string>;
async findWorkingFileName(repoPath: string, fileName: string): Promise<string>;
async findWorkingFileName(commitOrRepoPath: GitCommit | string, fileName?: string): Promise<string> {
let repoPath: string;
if (typeof commitOrRepoPath === 'string') {
async findWorkingFileName(commit: GitCommit): Promise<string | undefined>;
async findWorkingFileName(repoPath: string | undefined, fileName: string): Promise<string | undefined>;
async findWorkingFileName(commitOrRepoPath: GitCommit | string | undefined, fileName?: string): Promise<string | undefined> {
let repoPath: string | undefined;
if (commitOrRepoPath === undefined || typeof commitOrRepoPath === 'string') {
repoPath = commitOrRepoPath;
if (fileName === undefined) throw new Error('Invalid fileName');
[fileName] = Git.splitPath(fileName, repoPath);
}
else {
@ -298,15 +294,15 @@ export class GitService extends Disposable {
}
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;
}
}
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 entry = this._gitCache.get(cacheKey);
@ -321,7 +317,7 @@ export class GitService extends Disposable {
const fileName = uri.fsPath;
let entry: GitCacheEntry | undefined;
if (this.UseGitCaching && !uri.sha) {
if (this.UseCaching && !uri.sha) {
const cacheKey = this.getCacheEntryKey(fileName);
entry = this._gitCache.get(cacheKey);
@ -347,7 +343,7 @@ export class GitService extends Disposable {
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 ignore = await this._gitignore;
@ -387,12 +383,16 @@ export class GitService extends Disposable {
async getBlameForLine(uri: GitUri, line: number): Promise<IGitBlameLine | undefined> {
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 blameLine = blame && blame.lines[line];
if (!blameLine) return undefined;
if (blame === undefined) return undefined;
const blameLine = blame.lines[line];
if (blameLine === undefined) return undefined;
const commit = blame.commits.get(blameLine.sha);
if (commit === undefined) return undefined;
return {
author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }),
commit: commit,
@ -454,7 +454,7 @@ export class GitService extends Disposable {
commits.set(c.sha, commit);
let author = authors.get(commit.author);
if (!author) {
if (author === undefined) {
author = {
name: commit.author,
lineCount: 0
@ -494,14 +494,14 @@ export class GitService extends Disposable {
const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration);
locations.push(new Location(uri, new Position(0, 0)));
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;
}
async getBranch(repoPath: string): Promise<GitBranch> {
async getBranch(repoPath: string): Promise<GitBranch | undefined> {
Logger.log(`getBranch('${repoPath}')`);
const data = await Git.branch(repoPath, false);
@ -533,10 +533,10 @@ export class GitService extends Disposable {
return entry && entry.uri;
}
async getLogCommit(repoPath: string, 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, fileName: string, shaOrOptions?: string | { firstIfMissing?: boolean, previous?: boolean }, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise<GitLogCommit | undefined> {
let sha: string;
async getLogCommit(repoPath: string | undefined, fileName: 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 | undefined, fileName: string, shaOrOptions?: string | undefined | { firstIfMissing?: boolean, previous?: boolean }, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise<GitLogCommit | undefined> {
let sha: string | undefined = undefined;
if (typeof shaOrOptions === 'string') {
sha = shaOrOptions;
}
@ -578,7 +578,7 @@ export class GitService extends Disposable {
maxCount = this.config.advanced.maxQuickHistory || 0;
}
let searchArgs: string[];
let searchArgs: string[] | undefined = undefined;
switch (searchBy) {
case GitRepoSearchBy.Author:
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})`);
let entry: GitCacheEntry | undefined;
if (this.UseGitCaching && !sha && !range && !maxCount && !reverse) {
if (this.UseCaching && !sha && !range && !maxCount && !reverse) {
const cacheKey = this.getCacheEntryKey(fileName);
entry = this._gitCache.get(cacheKey);
@ -634,7 +634,7 @@ export class GitService extends Disposable {
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 ignore = await this._gitignore;
@ -683,7 +683,7 @@ export class GitService extends Disposable {
const uri = GitService.toReferenceGitContentUri(c, i + 1, commitCount, c.originalFileName, decoration);
locations.push(new Location(uri, new Position(0, 0)));
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}')`);
if (this.UseGitCaching) {
if (this.UseCaching) {
const remotes = this._remotesCache.get(repoPath);
if (remotes !== undefined) return remotes;
}
const data = await Git.remote(repoPath);
const remotes = data.split('\n').filter(_ => !!_).map(_ => new GitRemote(_));
if (this.UseGitCaching) {
if (this.UseCaching) {
this._remotesCache.set(repoPath, remotes);
}
return remotes;
}
getRepoPath(cwd: string): Promise<string> {
return Git.getRepoPath(cwd);
return GitService.getRepoPath(cwd);
}
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;
}
async getStashList(repoPath: string): Promise<IGitStash> {
async getStashList(repoPath: string): Promise<IGitStash | undefined> {
Logger.log(`getStash('${repoPath}')`);
const data = await Git.stash_list(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}')`);
const porcelainVersion = Git.validateVersion(2, 11) ? 2 : 1;
const data = await Git.status_file(repoPath, fileName, 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}')`);
const porcelainVersion = Git.validateVersion(2, 11) ? 2 : 1;
@ -750,12 +752,12 @@ export class GitService extends Disposable {
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})`);
const file = await Git.getVersionedFile(repoPath, fileName, sha);
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);
return file;
}
@ -792,14 +794,14 @@ export class GitService extends Disposable {
async isFileUncommitted(uri: GitUri): Promise<boolean> {
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;
}
async isTracked(uri: GitUri): Promise<boolean> {
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;
}
@ -849,7 +851,7 @@ export class GitService extends Disposable {
return Git.gitInfo().version;
}
static getRepoPath(cwd: string): Promise<string> {
static getRepoPath(cwd: string | undefined): Promise<string> {
return Git.getRepoPath(cwd);
}
@ -874,15 +876,15 @@ export class GitService extends Disposable {
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(shaOrcommit: string | GitCommit, shortSha?: string, fileName?: string, repoPath?: string, originalFileName?: string): Uri {
let data: IGitUriData;
if (typeof shaOrcommit === 'string') {
data = GitService._toGitUriData({
sha: shaOrcommit,
fileName: fileName,
repoPath: repoPath,
fileName: fileName!,
repoPath: repoPath!,
originalFileName: originalFileName
});
}
@ -892,8 +894,8 @@ export class GitService extends Disposable {
shortSha = shaOrcommit.shortSha;
}
const extension = path.extname(fileName);
return Uri.parse(`${DocumentSchemes.GitLensGit}:${path.basename(fileName, extension)}:${shortSha}${extension}?${JSON.stringify(data)}`);
const extension = path.extname(fileName!);
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 {
@ -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
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 {

+ 1
- 0
src/logger.ts View File

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

+ 9
- 8
src/quickPicks/branchHistory.ts View File

@ -35,7 +35,7 @@ export class BranchHistoryQuickPick {
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]));
let previousPageCommand: CommandQuickPickItem;
let previousPageCommand: CommandQuickPickItem | undefined = undefined;
if (log.truncated || log.sha) {
if (log.truncated) {
@ -72,13 +72,14 @@ export class BranchHistoryQuickPick {
}, Commands.ShowQuickBranchHistory, [uri, branch, log.maxCount, goBackCommand, undefined, nextPageCommand]);
const last = Iterables.last(log.commits.values());
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]);
items.splice(0, 0, previousPageCommand);
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]);
items.splice(0, 0, previousPageCommand);
}
}
}

+ 1
- 1
src/quickPicks/branches.ts View File

@ -11,7 +11,7 @@ export class BranchQuickPickItem implements QuickPickItem {
constructor(public branch: GitBranch) {
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' : '';
}
}

+ 11
- 11
src/quickPicks/commitDetails.ts View File

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

+ 13
- 9
src/quickPicks/commitFileDetails.ts View File

@ -3,7 +3,7 @@ import { Arrays, Iterables } from '../system';
import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode';
import { Commands, Keyboard, KeyNoopCommand } from '../commands';
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 * as moment from 'moment';
import * as path from 'path';
@ -14,7 +14,7 @@ export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPick
let description: string;
let uri: Uri;
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})`;
}
else {
@ -51,8 +51,10 @@ export class CommitFileDetailsQuickPick {
const isUncommitted = commit.isUncommitted;
if (isUncommitted) {
// 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 });
if (!commit) return undefined;
const c = await git.getLogCommit(undefined, commit.uri.fsPath, { previous: true });
if (c === undefined) return undefined;
commit = c;
}
if (!stash) {
@ -99,7 +101,7 @@ export class CommitFileDetailsQuickPick {
items.push(new OpenRemotesCommandQuickPickItem(remotes, 'file', commit.fileName, undefined, commit, currentCommand));
}
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));
}
}
@ -122,13 +124,13 @@ export class CommitFileDetailsQuickPick {
items.splice(0, 0, goBackCommand);
}
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>);
let previousCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>) | undefined = undefined;
let nextCommand: CommandQuickPickItem | (() => Promise<CommandQuickPickItem>) | undefined = undefined;
if (!stash) {
// If we have the full history, we are good
if (fileLog && !fileLog.truncated && !fileLog.sha) {
previousCommand = commit.previousSha && 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]);
previousCommand = commit.previousSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.previousUri, commit.previousSha, undefined, goBackCommand, fileLog]);
nextCommand = commit.nextSha === undefined ? undefined : new KeyCommandQuickPickItem(Commands.ShowQuickCommitFileDetails, [commit.nextUri, commit.nextSha, undefined, goBackCommand, fileLog]);
}
else {
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 (!c || !c.previousSha) {
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);
// Since we exclude merge commits in file log, just grab the first returned commit
if (!c && commit.isMerge) {

+ 13
- 10
src/quickPicks/common.ts View File

@ -8,7 +8,8 @@ import { GitCommit, GitLogCommit, GitStashCommit } from '../gitService';
import * as moment from 'moment';
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 {
@ -32,7 +33,7 @@ export function showQuickPickProgress(message: string, mapping?: KeyMapping, del
async function _showQuickPickProgress(message: string, cancellation: CancellationTokenSource, mapping?: KeyMapping) {
// 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 {
await window.showQuickPick(_getInfiniteCancellablePromise(cancellation), {
@ -64,21 +65,23 @@ export class CommandQuickPickItem implements QuickPickItem {
label: 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);
}
execute(): Thenable<{}> {
execute(): Thenable<{} | undefined> {
if (this.command === undefined) return Promise.resolve(undefined);
return commands.executeCommand(this.command, ...(this.args || []));
}
}
export class KeyCommandQuickPickItem extends CommandQuickPickItem {
constructor(protected command: Commands, protected args?: any[]) {
super({ label: undefined, description: undefined }, command, args);
constructor(command: Commands, args?: any[]) {
super({ label: '', description: '' } as QuickPickItem, command, args);
}
}
@ -88,7 +91,7 @@ export class OpenFileCommandQuickPickItem extends CommandQuickPickItem {
super(item, undefined, undefined);
}
async execute(pinned: boolean = false): Promise<{}> {
async execute(pinned: boolean = false): Promise<{} | undefined> {
return this.open(pinned);
}
@ -103,7 +106,7 @@ export class OpenFilesCommandQuickPickItem extends CommandQuickPickItem {
super(item, undefined, undefined);
}
async execute(): Promise<{}> {
async execute(): Promise<{} | undefined> {
for (const uri of this.uris) {
await openEditor(uri, true);
}
@ -126,7 +129,7 @@ export class CommitQuickPickItem implements QuickPickItem {
if (commit instanceof GitStashCommit) {
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()}`;
}
else {

+ 13
- 12
src/quickPicks/fileHistory.ts 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> {
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;
if (log.truncated || log.sha) {
@ -63,18 +63,19 @@ export class FileHistoryQuickPick {
}, Commands.ShowQuickFileHistory, [uri, undefined, log.maxCount, goBackCommand, undefined, nextPageCommand]);
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({
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]);
index++;
items.splice(0, 0, previousPageCommand);
index++;
items.splice(0, 0, previousPageCommand);
}
}
}
const branch = await git.getBranch(uri.repoPath);
const branch = await git.getBranch(uri.repoPath!);
const currentCommand = new CommandQuickPickItem({
label: `go back \u21A9`,
@ -85,7 +86,7 @@ export class FileHistoryQuickPick {
if (!goBackCommand) {
items.splice(index++, 0, new CommandQuickPickItem({
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,
[
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) {
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) {

+ 11
- 11
src/quickPicks/remotes.ts View File

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

+ 9
- 9
src/quickPicks/repoStatus.ts View File

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

+ 3
- 3
src/system/iterable.ts 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) {
if (predicate(item)) return item;
}
@ -76,8 +76,8 @@ export namespace Iterables {
return value;
}
export function last<T>(source: Iterable<T>): T {
let item: T;
export function last<T>(source: Iterable<T>): T | null {
let item: T | null = null;
for (item of source) { /* noop */ }
return item;
}

+ 6
- 6
src/whitespaceController.ts View File

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

+ 3
- 3
tsconfig.json View File

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

Loading…
Cancel
Save