Browse Source

Fixes errors from typescipt upgrade

Adds Arrays.filterMap
Reworks repo status message computation
main
Eric Amodio 7 years ago
parent
commit
20b6be9652
13 changed files with 167 additions and 79 deletions
  1. +3
    -3
      src/activeEditorTracker.ts
  2. +3
    -4
      src/annotations/blameAnnotationProvider.ts
  3. +11
    -7
      src/commands/externalDiff.ts
  4. +3
    -1
      src/commands/openChangedFiles.ts
  5. +19
    -3
      src/git/models/logCommit.ts
  6. +0
    -2
      src/git/parsers/blameParser.ts
  7. +3
    -2
      src/git/parsers/logParser.ts
  8. +3
    -1
      src/git/parsers/stashParser.ts
  9. +5
    -7
      src/quickPicks/commitDetails.ts
  10. +79
    -28
      src/quickPicks/repoStatus.ts
  11. +29
    -9
      src/system/array.ts
  12. +1
    -1
      src/system/iterable.ts
  13. +8
    -11
      src/views/gitExplorer.ts

+ 3
- 3
src/activeEditorTracker.ts View File

@ -7,7 +7,7 @@ import { BuiltInCommands } from './constants';
export class ActiveEditorTracker extends Disposable {
private _disposable: Disposable;
private _resolver: ((value?: TextEditor | PromiseLike<TextEditor>) => void) | undefined;
private _resolver: ((editor: TextEditor | undefined) => void) | undefined;
constructor() {
super(() => this.dispose());
@ -42,11 +42,11 @@ export class ActiveEditorTracker extends Disposable {
const editor = await new Promise<TextEditor>((resolve, reject) => {
let timer: any;
this._resolver = (e: TextEditor) => {
this._resolver = (editor: TextEditor | undefined) => {
if (timer) {
clearTimeout(timer as any);
timer = 0;
resolve(e);
resolve(editor);
}
};

+ 3
- 4
src/annotations/blameAnnotationProvider.ts View File

@ -1,5 +1,5 @@
'use strict';
import { Iterables } from '../system';
import { Arrays, Iterables } from '../system';
import { CancellationToken, Disposable, ExtensionContext, Hover, HoverProvider, languages, Position, Range, TextDocument, TextEditor, TextEditorDecorationType } from 'vscode';
import { FileAnnotationType } from './annotationController';
import { AnnotationProviderBase } from './annotationProvider';
@ -56,9 +56,8 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
return;
}
const highlightDecorationRanges = blame.lines
.filter(l => l.sha === sha)
.map(l => this.editor.document.validateRange(new Range(l.line, 0, l.line, 1000000)));
const highlightDecorationRanges = Arrays.filterMap(blame.lines,
l => l.sha !== sha ? this.editor.document.validateRange(new Range(l.line, 0, l.line, 1000000)) : undefined);
this.editor.setDecorations(this.highlightDecoration, highlightDecorationRanges);
}

+ 11
- 7
src/commands/externalDiff.ts View File

@ -1,4 +1,5 @@
'use strict';
import { Arrays } from '../system';
import { commands, SourceControlResourceState, Uri, window } from 'vscode';
import { Command, Commands } from './common';
import { BuiltInCommands } from '../constants';
@ -63,16 +64,14 @@ export class ExternalDiffCommand extends Command {
if (context.type === 'scm-states') {
args = { ...args };
args.files = context.scmResourceStates
.map<ExternalDiffFile>((r: Resource) => new ExternalDiffFile(r.resourceUri, r.resourceGroupType === ResourceGroupType.Index));
.map(r => new ExternalDiffFile(r.resourceUri, (r as Resource).resourceGroupType === ResourceGroupType.Index));
return this.execute(args);
} else if (context.type === 'scm-groups') {
const isModified = (status: Status): boolean => status === Status.BOTH_MODIFIED || status === Status.INDEX_MODIFIED || status === Status.MODIFIED;
}
else if (context.type === 'scm-groups') {
args = { ...args };
args.files = context.scmResourceGroups[0].resourceStates
.filter((r: Resource) => isModified(r.type))
.map<ExternalDiffFile>((r: Resource) => new ExternalDiffFile(r.resourceUri, r.resourceGroupType === ResourceGroupType.Index));
args.files = Arrays.filterMap(context.scmResourceGroups[0].resourceStates,
r => this.isModified(r) ? new ExternalDiffFile(r.resourceUri, (r as Resource).resourceGroupType === ResourceGroupType.Index) : undefined);
return this.execute(args);
}
@ -80,6 +79,11 @@ export class ExternalDiffCommand extends Command {
return this.execute(args);
}
private isModified(resource: SourceControlResourceState) {
const status = (resource as Resource).type;
return status === Status.BOTH_MODIFIED || status === Status.INDEX_MODIFIED || status === Status.MODIFIED;
}
async execute(args: ExternalDiffCommandArgs = {}) {
try {
const diffTool = await this.git.getConfig('diff.tool');

+ 3
- 1
src/commands/openChangedFiles.ts View File

@ -1,4 +1,5 @@
'use strict';
import { Arrays } from '../system';
import { TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri, openEditor } from './common';
import { GitService } from '../gitService';
@ -30,7 +31,8 @@ export class OpenChangedFilesCommand extends ActiveEditorCommand {
const status = await this.git.getStatusForRepo(repoPath);
if (status === undefined) return window.showWarningMessage(`Unable to open changed files`);
args.uris = status.files.filter(f => f.status !== 'D').map(f => f.Uri);
args.uris = Arrays.filterMap(status.files,
f => f.status !== 'D' ? f.Uri : undefined);
}
for (const uri of args.uris) {

+ 19
- 3
src/git/models/logCommit.ts View File

@ -63,9 +63,25 @@ export class GitLogCommit extends GitCommit {
}
getDiffStatus(): string {
const added = this.fileStatuses.filter(f => f.status === 'A' || f.status === '?').length;
const deleted = this.fileStatuses.filter(f => f.status === 'D').length;
const changed = this.fileStatuses.filter(f => f.status !== 'A' && f.status !== '?' && f.status !== 'D').length;
let added = 0;
let deleted = 0;
let changed = 0;
for (const f of this.fileStatuses) {
switch (f.status) {
case 'A':
case '?':
added++;
break;
case 'D':
deleted++;
break;
default:
changed++;
break;
}
}
return `+${added} ~${changed} -${deleted}`;
}

+ 0
- 2
src/git/parsers/blameParser.ts View File

@ -37,11 +37,9 @@ export class GitBlameParser {
let line: string;
let lineParts: string[];
let i = -1;
let first = true;
for (line of Strings.lines(data)) {
i++;
lineParts = line.split(' ');
if (lineParts.length < 2) continue;

+ 3
- 2
src/git/parsers/logParser.ts View File

@ -1,5 +1,5 @@
'use strict';
import { Strings } from '../../system';
import { Arrays, Strings } from '../../system';
import { Range } from 'vscode';
import { Git, GitAuthor, GitCommitType, GitLog, GitLogCommit, GitStatusFileStatus, IGitStatusFile } from './../git';
// import { Logger } from '../../logger';
@ -170,7 +170,8 @@ export class GitLogParser {
}
if (entry.fileStatuses) {
entry.fileName = entry.fileStatuses.filter(f => !!f.fileName).map(f => f.fileName).join(', ');
entry.fileName = Arrays.filterMap(entry.fileStatuses,
f => !!f.fileName ? f.fileName : undefined).join(', ');
}
}
else {

+ 3
- 1
src/git/parsers/stashParser.ts View File

@ -1,4 +1,5 @@
'use strict';
import { Arrays } from '../../system';
import { Git, GitStash, GitStashCommit, GitStatusFileStatus, IGitStatusFile } from './../git';
// import { Logger } from '../../logger';
@ -120,7 +121,8 @@ export class GitStashParser {
}
if (entry.fileStatuses) {
entry.fileNames = entry.fileStatuses.filter(f => !!f.fileName).map(f => f.fileName).join(', ');
entry.fileNames = Arrays.filterMap(entry.fileStatuses,
f => !!f.fileName ? f.fileName : undefined).join(', ');
}
entries.push(entry);

+ 5
- 7
src/quickPicks/commitDetails.ts View File

@ -1,5 +1,5 @@
'use strict';
import { Iterables, Strings } from '../system';
import { Arrays, Iterables, Strings } from '../system';
import { commands, QuickPickOptions, TextDocumentShowOptions, Uri, window } from 'vscode';
import { Commands, CopyMessageToClipboardCommandArgs, CopyShaToClipboardCommandArgs, DiffDirectoryCommandCommandArgs, DiffWithPreviousCommandArgs, ShowQuickCommitDetailsCommandArgs, StashApplyCommandArgs, StashDeleteCommandArgs } from '../commands';
import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, KeyCommandQuickPickItem, OpenFileCommandQuickPickItem, OpenFilesCommandQuickPickItem, QuickPickItem } from './common';
@ -74,9 +74,8 @@ export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPi
item?: QuickPickItem
) {
const repoPath = commit.repoPath;
const uris = commit.fileStatuses
.filter(s => s.status !== 'D')
.map(s => GitUri.fromFileStatus(s, repoPath));
const uris = Arrays.filterMap(commit.fileStatuses,
f => f.status !== 'D' ? GitUri.fromFileStatus(f, repoPath) : undefined);
super(uris, item || {
label: `$(file-symlink-file) Open Changed Files`,
@ -92,9 +91,8 @@ export class OpenCommitFileRevisionsCommandQuickPickItem extends OpenFilesComman
commit: GitLogCommit,
item?: QuickPickItem
) {
const uris = commit.fileStatuses
.filter(s => s.status !== 'D')
.map(s => GitService.toGitContentUri(commit.sha, s.fileName, commit.repoPath, s.originalFileName));
const uris = Arrays.filterMap(commit.fileStatuses,
f => f.status !== 'D' ? GitService.toGitContentUri(commit.sha, f.fileName, commit.repoPath, f.originalFileName) : undefined);
super(uris, item || {
label: `$(file-symlink-file) Open Changed Revisions`,

+ 79
- 28
src/quickPicks/repoStatus.ts View File

@ -56,34 +56,84 @@ export class OpenStatusFilesCommandQuickPickItem extends CommandQuickPickItem {
}
}
interface ComputedStatus {
staged: number;
stagedAddsAndChanges: GitStatusFile[];
stagedStatus: string;
unstaged: number;
unstagedAddsAndChanges: GitStatusFile[];
unstagedStatus: string;
}
export class RepoStatusQuickPick {
static async show(status: GitStatus, goBackCommand?: CommandQuickPickItem): Promise<OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem | undefined> {
// Sort the status by staged and then filename
const files = status.files;
files.sort((a, b) => (a.staged ? -1 : 1) - (b.staged ? -1 : 1) || a.fileName.localeCompare(b.fileName));
private static computeStatus(files: GitStatusFile[]): ComputedStatus {
let stagedAdds = 0;
let unstagedAdds = 0;
let stagedChanges = 0;
let unstagedChanges = 0;
let stagedDeletes = 0;
let unstagedDeletes = 0;
const added = files.filter(f => f.status === 'A' || f.status === '?');
const deleted = files.filter(f => f.status === 'D');
const changed = files.filter(f => f.status !== 'A' && f.status !== '?' && f.status !== 'D');
const stagedAddsAndChanges: GitStatusFile[] = [];
const unstagedAddsAndChanges: GitStatusFile[] = [];
const hasStaged = files.some(f => f.staged);
for (const f of files) {
switch (f.status) {
case 'A':
case '?':
if (f.staged) {
stagedAdds++;
stagedAddsAndChanges.push(f);
}
else {
unstagedAdds++;
unstagedAddsAndChanges.push(f);
}
break;
let stagedStatus = '';
let unstagedStatus = '';
if (hasStaged) {
const stagedAdded = added.filter(f => f.staged).length;
const stagedChanged = changed.filter(f => f.staged).length;
const stagedDeleted = deleted.filter(f => f.staged).length;
case 'D':
if (f.staged) {
stagedDeletes++;
}
else {
unstagedDeletes++;
}
break;
stagedStatus = `+${stagedAdded} ~${stagedChanged} -${stagedDeleted}`;
unstagedStatus = `+${added.length - stagedAdded} ~${changed.length - stagedChanged} -${deleted.length - stagedDeleted}`;
}
else {
unstagedStatus = `+${added.length} ~${changed.length} -${deleted.length}`;
default:
if (f.staged) {
stagedChanges++;
stagedAddsAndChanges.push(f);
}
else {
unstagedChanges++;
unstagedAddsAndChanges.push(f);
}
break;
}
}
const items = Array.from(Iterables.map(files, s => new OpenStatusFileCommandQuickPickItem(s))) as (OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem)[];
const staged = stagedAdds + stagedChanges + stagedDeletes;
const unstaged = unstagedAdds + unstagedChanges + unstagedDeletes;
return {
staged: staged,
stagedStatus: staged > 0 ? `+${stagedAdds} ~${stagedChanges} -${stagedDeletes}` : '',
stagedAddsAndChanges: stagedAddsAndChanges,
unstaged: unstaged,
unstagedStatus: unstaged > 0 ? `+${unstagedAdds} ~${unstagedChanges} -${unstagedDeletes}` : '',
unstagedAddsAndChanges: unstagedAddsAndChanges
};
}
static async show(status: GitStatus, goBackCommand?: CommandQuickPickItem): Promise<OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem | undefined> {
// Sort the status by staged and then filename
const files = status.files;
files.sort((a, b) => (a.staged ? -1 : 1) - (b.staged ? -1 : 1) || a.fileName.localeCompare(b.fileName));
const items = [...Iterables.map(files, s => new OpenStatusFileCommandQuickPickItem(s))] as (OpenStatusFileCommandQuickPickItem | OpenStatusFilesCommandQuickPickItem | CommandQuickPickItem)[];
const currentCommand = new CommandQuickPickItem({
label: `go back ${GlyphChars.ArrowBack}`,
@ -95,13 +145,14 @@ export class RepoStatusQuickPick {
} as ShowQuickRepoStatusCommandArgs
]);
if (hasStaged) {
const computed = this.computeStatus(files);
if (computed.staged > 0) {
let index = 0;
const unstagedIndex = files.findIndex(f => !f.staged);
const unstagedIndex = computed.unstaged > 0 ? files.findIndex(f => !f.staged) : -1;
if (unstagedIndex > -1) {
items.splice(unstagedIndex, 0, new CommandQuickPickItem({
label: `Unstaged Files`,
description: unstagedStatus
description: computed.unstagedStatus
}, Commands.ShowQuickRepoStatus, [
undefined,
{
@ -109,12 +160,12 @@ export class RepoStatusQuickPick {
} as ShowQuickRepoStatusCommandArgs
]));
items.splice(unstagedIndex, 0, new OpenStatusFilesCommandQuickPickItem(files.filter(f => f.status !== 'D' && f.staged), {
items.splice(unstagedIndex, 0, new OpenStatusFilesCommandQuickPickItem(computed.stagedAddsAndChanges, {
label: `${GlyphChars.Space.repeat(4)} $(file-symlink-file) Open Staged Files`,
description: ''
}));
items.push(new OpenStatusFilesCommandQuickPickItem(files.filter(f => f.status !== 'D' && !f.staged), {
items.push(new OpenStatusFilesCommandQuickPickItem(computed.unstagedAddsAndChanges, {
label: `${GlyphChars.Space.repeat(4)} $(file-symlink-file) Open Unstaged Files`,
description: ''
}));
@ -122,7 +173,7 @@ export class RepoStatusQuickPick {
items.splice(index++, 0, new CommandQuickPickItem({
label: `Staged Files`,
description: stagedStatus
description: computed.stagedStatus
}, Commands.ShowQuickRepoStatus, [
undefined,
{
@ -133,7 +184,7 @@ export class RepoStatusQuickPick {
else if (files.some(f => !f.staged)) {
items.splice(0, 0, new CommandQuickPickItem({
label: `Unstaged Files`,
description: unstagedStatus
description: computed.unstagedStatus
}, Commands.ShowQuickRepoStatus, [
undefined,
{
@ -143,7 +194,7 @@ export class RepoStatusQuickPick {
}
if (files.length) {
items.push(new OpenStatusFilesCommandQuickPickItem(files.filter(f => f.status !== 'D')));
items.push(new OpenStatusFilesCommandQuickPickItem(computed.stagedAddsAndChanges.concat(computed.unstagedAddsAndChanges)));
items.push(new CommandQuickPickItem({
label: '$(x) Close Unchanged Files',
description: ''

+ 29
- 9
src/system/array.ts View File

@ -2,21 +2,41 @@
import { Objects } from './object';
export namespace Arrays {
export function countUniques<T>(array: T[], accessor: (item: T) => string): { [key: string]: number } {
export function countUniques<T>(source: T[], accessor: (item: T) => string): { [key: string]: number } {
const uniqueCounts = Object.create(null);
for (const item of array) {
for (const item of source) {
const value = accessor(item);
uniqueCounts[value] = (uniqueCounts[value] || 0) + 1;
}
return uniqueCounts;
}
export function groupBy<T>(array: T[], accessor: (item: T) => string): { [key: string]: T[] } {
return array.reduce((previous, current) => {
export function filterMap<T, TMapped>(source: T[], predicateMapper: (item: T) => TMapped | null | undefined): TMapped[] {
return source.reduce((accumulator, current) => {
const mapped = predicateMapper(current);
if (mapped != null) {
accumulator.push(mapped);
}
return accumulator;
}, [] as any);
}
export async function filterMapAsync<T, TMapped>(source: T[], predicateMapper: (item: T) => Promise<TMapped | null | undefined>): Promise<TMapped[]> {
return source.reduce(async (accumulator, current) => {
const mapped = await predicateMapper(current);
if (mapped != null) {
accumulator.push(mapped);
}
return accumulator;
}, [] as any);
}
export function groupBy<T>(source: T[], accessor: (item: T) => string): { [key: string]: T[] } {
return source.reduce((groupings, current) => {
const value = accessor(current);
previous[value] = previous[value] || [];
previous[value].push(current);
return previous;
groupings[value] = groupings[value] || [];
groupings[value].push(current);
return groupings;
}, Object.create(null));
}
@ -110,9 +130,9 @@ export namespace Arrays {
return root;
}
export function uniqueBy<T>(array: T[], accessor: (item: T) => any, predicate?: (item: T) => boolean): T[] {
export function uniqueBy<T>(source: T[], accessor: (item: T) => any, predicate?: (item: T) => boolean): T[] {
const uniqueValues = Object.create(null);
return array.filter(item => {
return source.filter(item => {
const value = accessor(item);
if (uniqueValues[value]) return false;

+ 1
- 1
src/system/iterable.ts View File

@ -17,7 +17,7 @@ export namespace Iterables {
export function* filterMap<T, TMapped>(source: Iterable<T> | IterableIterator<T>, predicateMapper: (item: T) => TMapped | undefined | null): Iterable<TMapped> {
for (const item of source) {
const mapped = predicateMapper(item);
if (mapped) yield mapped;
if (mapped != null) yield mapped;
}
}

+ 8
- 11
src/views/gitExplorer.ts View File

@ -1,5 +1,5 @@
'use strict';
import { Functions, Objects } from '../system';
import { Arrays, Functions, Objects } from '../system';
import { commands, Disposable, Event, EventEmitter, ExtensionContext, TextDocumentShowOptions, TextEditor, TreeDataProvider, TreeItem, Uri, window, workspace } from 'vscode';
import { Commands, DiffWithCommandArgs, DiffWithCommandArgsRevision, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
import { UriComparer } from '../comparers';
@ -334,28 +334,25 @@ export class GitExplorer implements TreeDataProvider {
private async openChangedFileChangesWithWorking(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
const repoPath = node.commit.repoPath;
const uris = node.commit.fileStatuses
.filter(s => s.status !== 'D')
.map(s => GitUri.fromFileStatus(s, repoPath));
const uris = Arrays.filterMap(node.commit.fileStatuses,
f => f.status !== 'D' ? GitUri.fromFileStatus(f, repoPath) : undefined);
for (const uri of uris) {
await this.openDiffWith(repoPath,
{ uri: uri, sha: node.commit.sha },
{ uri: uri, sha: '' }, options);
await this.openDiffWith(repoPath, { uri: uri, sha: node.commit.sha }, { uri: uri, sha: '' }, options);
}
}
private async openChangedFiles(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
const repoPath = node.commit.repoPath;
const uris = node.commit.fileStatuses.filter(s => s.status !== 'D').map(s => GitUri.fromFileStatus(s, repoPath));
const uris = Arrays.filterMap(node.commit.fileStatuses,
f => f.status !== 'D' ? GitUri.fromFileStatus(f, repoPath) : undefined);
for (const uri of uris) {
await openEditor(uri, options);
}
}
private async openChangedFileRevisions(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
const uris = node.commit.fileStatuses
.filter(s => s.status !== 'D')
.map(s => GitService.toGitContentUri(node.commit.sha, s.fileName, node.commit.repoPath, s.originalFileName));
const uris = Arrays.filterMap(node.commit.fileStatuses,
f => f.status !== 'D' ? GitService.toGitContentUri(node.commit.sha, f.fileName, node.commit.repoPath, f.originalFileName) : undefined);
for (const uri of uris) {
await openEditor(uri, options);
}

Loading…
Cancel
Save