diff --git a/src/activeEditorTracker.ts b/src/activeEditorTracker.ts index 9ff6ee8..1ded701 100644 --- a/src/activeEditorTracker.ts +++ b/src/activeEditorTracker.ts @@ -6,7 +6,7 @@ import { BuiltInCommands } from './constants'; export class ActiveEditorTracker extends Disposable { private _disposable: Disposable; - private _resolver: (value?: TextEditor | PromiseLike) => void; + private _resolver: ((value?: TextEditor | PromiseLike) => 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); } diff --git a/src/blameActiveLineController.ts b/src/blameActiveLineController.ts index b677162..6e01792 100644 --- a/src/blameActiveLineController.ts +++ b/src/blameActiveLineController.ts @@ -54,38 +54,38 @@ export class BlameActiveLineController extends Disposable { } private _onConfigurationChanged() { - const config = workspace.getConfiguration().get(ExtensionKey); + const cfg = workspace.getConfiguration().get(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]); + } } } } \ No newline at end of file diff --git a/src/blameAnnotationController.ts b/src/blameAnnotationController.ts index e6f376c..8bb55f3 100644 --- a/src/blameAnnotationController.ts +++ b/src/blameAnnotationController.ts @@ -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 = 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('blame'); + const cfg = workspace.getConfiguration(ExtensionKey).get('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) { diff --git a/src/blameAnnotationProvider.ts b/src/blameAnnotationProvider.ts index 552444f..0e9bba1 100644 --- a/src/blameAnnotationProvider.ts +++ b/src/blameAnnotationProvider.ts @@ -24,7 +24,7 @@ export class BlameAnnotationProvider extends Disposable { this._blame = this.git.getBlameForFile(this.uri); - this._config = workspace.getConfiguration(ExtensionKey).get('blame'); + this._config = workspace.getConfiguration(ExtensionKey).get('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 { - let whitespacePromise: Promise; + let whitespacePromise: Promise | 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) { diff --git a/src/commands/closeUnchangedFiles.ts b/src/commands/closeUnchangedFiles.ts index 2377237..9a25056 100644 --- a/src/commands/closeUnchangedFiles.ts +++ b/src/commands/closeUnchangedFiles.ts @@ -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(); diff --git a/src/commands/common.ts b/src/commands/common.ts index 2616a7b..15a0977 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -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; } diff --git a/src/commands/diffDirectory.ts b/src/commands/diffDirectory.ts index ccce301..f8209f5 100644 --- a/src/commands/diffDirectory.ts +++ b/src/commands/diffDirectory.ts @@ -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; diff --git a/src/commands/diffLineWithPrevious.ts b/src/commands/diffLineWithPrevious.ts index ce401ae..f8da141 100644 --- a/src/commands/diffLineWithPrevious.ts +++ b/src/commands/diffLineWithPrevious.ts @@ -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' }); } diff --git a/src/commands/diffLineWithWorking.ts b/src/commands/diffLineWithWorking.ts index 0057ae3..1771aa3 100644 --- a/src/commands/diffLineWithWorking.ts +++ b/src/commands/diffLineWithWorking.ts @@ -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; } } diff --git a/src/commands/diffWithBranch.ts b/src/commands/diffWithBranch.ts index c46dd95..88345bd 100644 --- a/src/commands/diffWithBranch.ts +++ b/src/commands/diffWithBranch.ts @@ -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) { diff --git a/src/commands/diffWithNext.ts b/src/commands/diffWithNext.ts index 4b95e8f..6db8264 100644 --- a/src/commands/diffWithNext.ts +++ b/src/commands/diffWithNext.ts @@ -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) { diff --git a/src/commands/diffWithPrevious.ts b/src/commands/diffWithPrevious.ts index d1d542c..ced9b57 100644 --- a/src/commands/diffWithPrevious.ts +++ b/src/commands/diffWithPrevious.ts @@ -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' }); } diff --git a/src/commands/diffWithWorking.ts b/src/commands/diffWithWorking.ts index 7907d99..90f3992 100644 --- a/src/commands/diffWithWorking.ts +++ b/src/commands/diffWithWorking.ts @@ -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) { diff --git a/src/commands/keyboard.ts b/src/commands/keyboard.ts index 9906447..3869cb5 100644 --- a/src/commands/keyboard.ts +++ b/src/commands/keyboard.ts @@ -16,7 +16,7 @@ export const keys: Keys[] = [ '.' ]; -export declare type KeyMapping = { [id: string]: (QuickPickItem | (() => Promise)) }; +export declare type KeyMapping = { [id: string]: (QuickPickItem | (() => Promise) | 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 { diff --git a/src/commands/openCommitInRemote.ts b/src/commands/openCommitInRemote.ts index aae3901..425852e 100644 --- a/src/commands/openCommitInRemote.ts +++ b/src/commands/openCommitInRemote.ts @@ -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); diff --git a/src/commands/openFileInRemote.ts b/src/commands/openFileInRemote.ts index 5b8e98f..908e600 100644 --- a/src/commands/openFileInRemote.ts +++ b/src/commands/openFileInRemote.ts @@ -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'); diff --git a/src/commands/openInRemote.ts b/src/commands/openInRemote.ts index 2dde646..455a962 100644 --- a/src/commands/openInRemote.ts +++ b/src/commands/openInRemote.ts @@ -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`; diff --git a/src/commands/showCommitSearch.ts b/src/commands/showCommitSearch.ts index 56b6e33..c30624b 100644 --- a/src/commands/showCommitSearch.ts +++ b/src/commands/showCommitSearch.ts @@ -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) { diff --git a/src/commands/showQuickBranchHistory.ts b/src/commands/showQuickBranchHistory.ts index 26c8bfa..46832d6 100644 --- a/src/commands/showQuickBranchHistory.ts +++ b/src/commands/showQuickBranchHistory.ts @@ -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) { diff --git a/src/commands/showQuickCommitDetails.ts b/src/commands/showQuickCommitDetails.ts index 589c3fb..153e2eb 100644 --- a/src/commands/showQuickCommitDetails.ts +++ b/src/commands/showQuickCommitDetails.ts @@ -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; } diff --git a/src/commands/showQuickCommitFileDetails.ts b/src/commands/showQuickCommitFileDetails.ts index c95a869..964e689 100644 --- a/src/commands/showQuickCommitFileDetails.ts +++ b/src/commands/showQuickCommitFileDetails.ts @@ -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 diff --git a/src/commands/showQuickStashList.ts b/src/commands/showQuickStashList.ts index d0d2c22..ddeeaa6 100644 --- a/src/commands/showQuickStashList.ts +++ b/src/commands/showQuickStashList.ts @@ -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({ diff --git a/src/comparers.ts b/src/comparers.ts index 7030b00..7486fc1 100644 --- a/src/comparers.ts +++ b/src/comparers.ts @@ -7,9 +7,9 @@ abstract class Comparer { class UriComparer extends Comparer { - 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 { - 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 { - 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; diff --git a/src/configuration.ts b/src/configuration.ts index 1b1613d..7d07bb5 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -58,7 +58,7 @@ export interface ICodeLensConfig { } export interface ICodeLensLanguageLocation { - language: string; + language: string | undefined; location: CodeLensLocation; customSymbols?: string[]; } diff --git a/src/extension.ts b/src/extension.ts index d4e4dff..49757ae 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -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(ExtensionKey); - const gitPath = config.advanced.git; + const cfg = workspace.getConfiguration().get(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 diff --git a/src/git/git.ts b/src/git/git.ts index 6087902..24ec990 100644 --- a/src/git/git.ts +++ b/src/git/git.ts @@ -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('^', ''); diff --git a/src/git/gitContextTracker.ts b/src/git/gitContextTracker.ts index a40a61b..a31e4d2 100644 --- a/src/git/gitContextTracker.ts +++ b/src/git/gitContextTracker.ts @@ -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('enabled'); + const gitEnabled = workspace.getConfiguration('git').get('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); diff --git a/src/git/gitUri.ts b/src/git/gitUri.ts index afd0a5e..f051ca3 100644 --- a/src/git/gitUri.ts +++ b/src/git/gitUri.ts @@ -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) { diff --git a/src/git/models/commit.ts b/src/git/models/commit.ts index 8af3f29..5b85c3c 100644 --- a/src/git/models/commit.ts +++ b/src/git/models/commit.ts @@ -13,7 +13,7 @@ export interface IGitCommit { repoPath: string; sha: string; fileName: string; - author: string; + author?: string; date: Date; message: string; lines: IGitCommitLine[]; diff --git a/src/git/models/logCommit.ts b/src/git/models/logCommit.ts index 8334fc0..e6e9927 100644 --- a/src/git/models/logCommit.ts +++ b/src/git/models/logCommit.ts @@ -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}`; + } } \ No newline at end of file diff --git a/src/git/models/stashCommit.ts b/src/git/models/stashCommit.ts index eed62d9..5c5720b 100644 --- a/src/git/models/stashCommit.ts +++ b/src/git/models/stashCommit.ts @@ -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() { diff --git a/src/git/parsers/blameParser.ts b/src/git/parsers/blameParser.ts index 250ff59..266f5a8 100644 --- a/src/git/parsers/blameParser.ts +++ b/src/git/parsers/blameParser.ts @@ -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 = new Map(); // const values = diff --git a/src/git/parsers/logParser.ts b/src/git/parsers/logParser.ts index 3a830f8..538ee6c 100644 --- a/src/git/parsers/logParser.ts +++ b/src/git/parsers/logParser.ts @@ -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 = 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 = 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); } diff --git a/src/git/parsers/stashParser.ts b/src/git/parsers/stashParser.ts index e0ea0f0..ede6c19 100644 --- a/src/git/parsers/stashParser.ts +++ b/src/git/parsers/stashParser.ts @@ -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 = 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); } diff --git a/src/git/parsers/statusParser.ts b/src/git/parsers/statusParser.ts index da9f667..de9b78c 100644 --- a/src/git/parsers/statusParser.ts +++ b/src/git/parsers/statusParser.ts @@ -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; } } \ No newline at end of file diff --git a/src/git/remotes/factory.ts b/src/git/remotes/factory.ts index 0f2ef36..5b17253 100644 --- a/src/git/remotes/factory.ts +++ b/src/git/remotes/factory.ts @@ -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/, ''); diff --git a/src/git/remotes/provider.ts b/src/git/remotes/provider.ts index 787b106..6042e35 100644 --- a/src/git/remotes/provider.ts +++ b/src/git/remotes/provider.ts @@ -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<{}>; diff --git a/src/gitCodeLensProvider.ts b/src/gitCodeLensProvider.ts index 99a7810..523f5c0 100644 --- a/src/gitCodeLensProvider.ts +++ b/src/gitCodeLensProvider.ts @@ -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(ExtensionKey); + this._config = workspace.getConfiguration().get(ExtensionKey)!; } reset() { - this._config = workspace.getConfiguration().get(ExtensionKey); + this._config = workspace.getConfiguration().get(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 { 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; } diff --git a/src/gitContentProvider.ts b/src/gitContentProvider.ts index f1e908c..19ae9ea 100644 --- a/src/gitContentProvider.ts +++ b/src/gitContentProvider.ts @@ -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 ''; } } } \ No newline at end of file diff --git a/src/gitService.ts b/src/gitService.ts index ad10e88..bec303e 100644 --- a/src/gitService.ts +++ b/src/gitService.ts @@ -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 | undefined; + private _gitCache: Map; private _remotesCache: Map; private _cacheDisposable: Disposable | undefined; - private _uriCache: Map | undefined; + private _uriCache: Map; 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; - static EmptyPromise: Promise = Promise.resolve(undefined); + static EmptyPromise: Promise = 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(ExtensionKey); + const cfg = workspace.getConfiguration().get(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((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((resolve, reject) => fs.exists(path.resolve(repoPath, fileName), e => resolve(e))); } - async findNextCommit(repoPath: string, fileName: string, sha?: string): Promise { + async findNextCommit(repoPath: string, fileName: string, sha?: string): Promise { 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 { + async findNextFileName(repoPath: string | undefined, fileName: string, sha?: string): Promise { [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 { + async _findNextFileName(repoPath: string, fileName: string, sha?: string): Promise { 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; - async findWorkingFileName(repoPath: string, fileName: string): Promise; - async findWorkingFileName(commitOrRepoPath: GitCommit | string, fileName?: string): Promise { - let repoPath: string; - if (typeof commitOrRepoPath === 'string') { + async findWorkingFileName(commit: GitCommit): Promise; + async findWorkingFileName(repoPath: string | undefined, fileName: string): Promise; + async findWorkingFileName(commitOrRepoPath: GitCommit | string | undefined, fileName?: string): Promise { + 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 { - 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 { + private async _getBlameForFile(uri: GitUri, fileName: string, entry: GitCacheEntry | undefined): Promise { 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 { 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 { + async getBranch(repoPath: string): Promise { 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; - async getLogCommit(repoPath: string, fileName: string, sha: string, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise; - async getLogCommit(repoPath: string, fileName: string, shaOrOptions?: string | { firstIfMissing?: boolean, previous?: boolean }, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise { - let sha: string; + async getLogCommit(repoPath: string | undefined, fileName: string, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise; + async getLogCommit(repoPath: string | undefined, fileName: string, sha: string | undefined, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise; + async getLogCommit(repoPath: string | undefined, fileName: string, shaOrOptions?: string | undefined | { firstIfMissing?: boolean, previous?: boolean }, options?: { firstIfMissing?: boolean, previous?: boolean }): Promise { + 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 { + getLogForFile(repoPath: string | undefined, fileName: string, sha?: string, maxCount?: number, range?: Range, reverse: boolean = false): Promise { 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 { + private async _getLogForFile(repoPath: string | undefined, fileName: string, sha: string | undefined, range: Range | undefined, maxCount: number | undefined, reverse: boolean, entry: GitCacheEntry | undefined): Promise { 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 { - return Git.getRepoPath(cwd); + return GitService.getRepoPath(cwd); } async getRepoPathFromFile(fileName: string): Promise { @@ -724,24 +724,26 @@ export class GitService extends Disposable { return (await GitUri.fromUri(uri, this)).repoPath || this.repoPath; } - async getStashList(repoPath: string): Promise { + async getStashList(repoPath: string): Promise { Logger.log(`getStash('${repoPath}')`); const data = await Git.stash_list(repoPath); return GitStashParser.parse(data, repoPath); } - async getStatusForFile(repoPath: string, fileName: string): Promise { + async getStatusForFile(repoPath: string, fileName: string): Promise { 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 { + async getStatusForRepo(repoPath: string): Promise { 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 { 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 { 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 { + static getRepoPath(cwd: string | undefined): Promise { 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(commit: IGitUriData, index?: number, originalFileName?: string, decoration?: string): T { diff --git a/src/logger.ts b/src/logger.ts index 43b97d6..749f62d 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -19,6 +19,7 @@ let output: OutputChannel; function onConfigurationChanged() { const cfg = workspace.getConfiguration().get(ExtensionKey); + if (cfg === undefined) return; if (cfg.debug !== debug || cfg.outputLevel !== level) { debug = cfg.debug; diff --git a/src/quickPicks/branchHistory.ts b/src/quickPicks/branchHistory.ts index 1385cf8..029efc5 100644 --- a/src/quickPicks/branchHistory.ts +++ b/src/quickPicks/branchHistory.ts @@ -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); + } } } diff --git a/src/quickPicks/branches.ts b/src/quickPicks/branches.ts index 02f45e8..1c65cf1 100644 --- a/src/quickPicks/branches.ts +++ b/src/quickPicks/branches.ts @@ -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' : ''; } } diff --git a/src/quickPicks/commitDetails.ts b/src/quickPicks/commitDetails.ts index ece1f82..220ab56 100644 --- a/src/quickPicks/commitDetails.ts +++ b/src/quickPicks/commitDetails.ts @@ -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); - let nextCommand: CommandQuickPickItem | (() => Promise); + let previousCommand: CommandQuickPickItem | (() => Promise) | undefined = undefined; + let nextCommand: CommandQuickPickItem | (() => Promise) | 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 () => { diff --git a/src/quickPicks/commitFileDetails.ts b/src/quickPicks/commitFileDetails.ts index c43e24a..4013a70 100644 --- a/src/quickPicks/commitFileDetails.ts +++ b/src/quickPicks/commitFileDetails.ts @@ -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); - let nextCommand: CommandQuickPickItem | (() => Promise); + let previousCommand: CommandQuickPickItem | (() => Promise) | undefined = undefined; + let nextCommand: CommandQuickPickItem | (() => Promise) | 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) { diff --git a/src/quickPicks/common.ts b/src/quickPicks/common.ts index 9392415..ae0c444 100644 --- a/src/quickPicks/common.ts +++ b/src/quickPicks/common.ts @@ -8,7 +8,8 @@ import { GitCommit, GitLogCommit, GitStashCommit } from '../gitService'; import * as moment from 'moment'; export function getQuickPickIgnoreFocusOut() { - return !workspace.getConfiguration(ExtensionKey).get('advanced').quickPick.closeOnFocusOut; + const cfg = workspace.getConfiguration(ExtensionKey).get('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 { diff --git a/src/quickPicks/fileHistory.ts b/src/quickPicks/fileHistory.ts index fd2dee3..bc01df1 100644 --- a/src/quickPicks/fileHistory.ts +++ b/src/quickPicks/fileHistory.ts @@ -21,7 +21,7 @@ export class FileHistoryQuickPick { static async show(git: GitService, log: IGitLog, uri: GitUri, progressCancellation: CancellationTokenSource, goBackCommand?: CommandQuickPickItem, nextPageCommand?: CommandQuickPickItem): Promise { 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) { diff --git a/src/quickPicks/remotes.ts b/src/quickPicks/remotes.ts index b6a9728..22f6911 100644 --- a/src/quickPicks/remotes.ts +++ b/src/quickPicks/remotes.ts @@ -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({ diff --git a/src/quickPicks/repoStatus.ts b/src/quickPicks/repoStatus.ts index 678df5e..505e5dc 100644 --- a/src/quickPicks/repoStatus.ts +++ b/src/quickPicks/repoStatus.ts @@ -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])); } diff --git a/src/system/iterable.ts b/src/system/iterable.ts index 548bf32..7779960 100644 --- a/src/system/iterable.ts +++ b/src/system/iterable.ts @@ -29,7 +29,7 @@ export namespace Iterables { } } - export function find(source: Iterable | IterableIterator, predicate: (item: T) => boolean): T { + export function find(source: Iterable | IterableIterator, 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(source: Iterable): T { - let item: T; + export function last(source: Iterable): T | null { + let item: T | null = null; for (item of source) { /* noop */ } return item; } diff --git a/src/whitespaceController.ts b/src/whitespaceController.ts index 485e754..67a54ef 100644 --- a/src/whitespaceController.ts +++ b/src/whitespaceController.ts @@ -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('renderWhitespace'); + const inspection = workspace.getConfiguration('editor').inspect('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, diff --git a/tsconfig.json b/tsconfig.json index fcdeece..9b4d849 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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": [