Skip to content

Commit

Permalink
Merge branch 'dirtydiff'
Browse files Browse the repository at this point in the history
  • Loading branch information
joaomoreno committed Oct 12, 2017
2 parents c053a4b + 6bd76d0 commit 3070c0b
Show file tree
Hide file tree
Showing 18 changed files with 1,042 additions and 177 deletions.
30 changes: 29 additions & 1 deletion extensions/git/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,24 @@
"title": "%command.revertSelectedRanges%",
"category": "Git"
},
{
"command": "git.stageChange",
"title": "%command.stageChange%",
"category": "Git",
"icon": {
"light": "resources/icons/light/stage.svg",
"dark": "resources/icons/dark/stage.svg"
}
},
{
"command": "git.revertChange",
"title": "%command.revertChange%",
"category": "Git",
"icon": {
"light": "resources/icons/light/clean.svg",
"dark": "resources/icons/dark/clean.svg"
}
},
{
"command": "git.unstage",
"title": "%command.unstage%",
Expand Down Expand Up @@ -722,6 +740,16 @@
"group": "2_git@3",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff"
}
],
"scm/change/title": [
{
"command": "git.stageChange",
"when": "config.git.enabled && originalResourceScheme == git"
},
{
"command": "git.revertChange",
"when": "config.git.enabled && originalResourceScheme == git"
}
]
},
"configuration": {
Expand Down Expand Up @@ -836,4 +864,4 @@
"@types/node": "7.0.43",
"mocha": "^3.2.0"
}
}
}
2 changes: 2 additions & 0 deletions extensions/git/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"command.stageAll": "Stage All Changes",
"command.stageSelectedRanges": "Stage Selected Ranges",
"command.revertSelectedRanges": "Revert Selected Ranges",
"command.stageChange": "Stage Change",
"command.revertChange": "Revert Change",
"command.unstage": "Unstage Changes",
"command.unstageAll": "Unstage All Changes",
"command.unstageSelectedRanges": "Unstage Selected Ranges",
Expand Down
81 changes: 57 additions & 24 deletions extensions/git/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

'use strict';

import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation } from 'vscode';
import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation, TextEditor } from 'vscode';
import { Ref, RefType, Git, GitErrorCodes, Branch } from './git';
import { Repository, Resource, Status, CommitOptions, ResourceGroupType } from './repository';
import { Model } from './model';
Expand Down Expand Up @@ -556,14 +556,39 @@ export class CommandCenter {
await repository.add([]);
}

@command('git.stageChange')
async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];

if (!textEditor) {
return;
}

await this._stageChanges(textEditor, [changes[index]]);
}

@command('git.stageSelectedRanges', { diff: true })
async stageSelectedRanges(diffs: LineChange[]): Promise<void> {
async stageSelectedChanges(changes: LineChange[]): Promise<void> {
const textEditor = window.activeTextEditor;

if (!textEditor) {
return;
}

const modifiedDocument = textEditor.document;
const selectedLines = toLineRanges(textEditor.selections, modifiedDocument);
const selectedChanges = changes
.map(diff => selectedLines.reduce<LineChange | null>((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null))
.filter(d => !!d) as LineChange[];

if (!selectedChanges.length) {
return;
}

await this._stageChanges(textEditor, selectedChanges);
}

private async _stageChanges(textEditor: TextEditor, changes: LineChange[]): Promise<void> {
const modifiedDocument = textEditor.document;
const modifiedUri = modifiedDocument.uri;

Expand All @@ -573,50 +598,57 @@ export class CommandCenter {

const originalUri = toGitUri(modifiedUri, '~');
const originalDocument = await workspace.openTextDocument(originalUri);
const selectedLines = toLineRanges(textEditor.selections, modifiedDocument);
const selectedDiffs = diffs
.map(diff => selectedLines.reduce<LineChange | null>((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null))
.filter(d => !!d) as LineChange[];
const result = applyLineChanges(originalDocument, modifiedDocument, changes);

if (!selectedDiffs.length) {
await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result));
}

@command('git.revertChange')
async revertChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];

if (!textEditor) {
return;
}

const result = applyLineChanges(originalDocument, modifiedDocument, selectedDiffs);

await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result));
await this._revertChanges(textEditor, [...changes.slice(0, index), ...changes.slice(index + 1)]);
}

@command('git.revertSelectedRanges', { diff: true })
async revertSelectedRanges(diffs: LineChange[]): Promise<void> {
async revertSelectedRanges(changes: LineChange[]): Promise<void> {
const textEditor = window.activeTextEditor;

if (!textEditor) {
return;
}

const modifiedDocument = textEditor.document;
const modifiedUri = modifiedDocument.uri;
const selections = textEditor.selections;
const selectedChanges = changes.filter(change => {
const modifiedRange = change.modifiedEndLineNumber === 0
? new Range(modifiedDocument.lineAt(change.modifiedStartLineNumber - 1).range.end, modifiedDocument.lineAt(change.modifiedStartLineNumber).range.start)
: new Range(modifiedDocument.lineAt(change.modifiedStartLineNumber - 1).range.start, modifiedDocument.lineAt(change.modifiedEndLineNumber - 1).range.end);

if (modifiedUri.scheme !== 'file') {
return selections.every(selection => !selection.intersection(modifiedRange));
});

if (selectedChanges.length === changes.length) {
return;
}

const originalUri = toGitUri(modifiedUri, '~');
const originalDocument = await workspace.openTextDocument(originalUri);
const selections = textEditor.selections;
const selectedDiffs = diffs.filter(diff => {
const modifiedRange = diff.modifiedEndLineNumber === 0
? new Range(modifiedDocument.lineAt(diff.modifiedStartLineNumber - 1).range.end, modifiedDocument.lineAt(diff.modifiedStartLineNumber).range.start)
: new Range(modifiedDocument.lineAt(diff.modifiedStartLineNumber - 1).range.start, modifiedDocument.lineAt(diff.modifiedEndLineNumber - 1).range.end);
await this._revertChanges(textEditor, selectedChanges);
}

return selections.every(selection => !selection.intersection(modifiedRange));
});
private async _revertChanges(textEditor: TextEditor, changes: LineChange[]): Promise<void> {
const modifiedDocument = textEditor.document;
const modifiedUri = modifiedDocument.uri;

if (selectedDiffs.length === diffs.length) {
if (modifiedUri.scheme !== 'file') {
return;
}

const originalUri = toGitUri(modifiedUri, '~');
const originalDocument = await workspace.openTextDocument(originalUri);
const basename = path.basename(modifiedUri.fsPath);
const message = localize('confirm revert', "Are you sure you want to revert the selected changes in {0}?", basename);
const yes = localize('revert', "Revert Changes");
Expand All @@ -626,10 +658,11 @@ export class CommandCenter {
return;
}

const result = applyLineChanges(originalDocument, modifiedDocument, selectedDiffs);
const result = applyLineChanges(originalDocument, modifiedDocument, changes);
const edit = new WorkspaceEdit();
edit.replace(modifiedUri, new Range(new Position(0, 0), modifiedDocument.lineAt(modifiedDocument.lineCount - 1).range.end), result);
workspace.applyEdit(edit);
await modifiedDocument.save();
}

@command('git.unstage')
Expand Down
19 changes: 14 additions & 5 deletions extensions/git/src/contentProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import { workspace, Uri, Disposable, Event, EventEmitter, window } from 'vscode';
import { debounce, throttle } from './decorators';
import { fromGitUri } from './uri';
import { Model, ModelChangeEvent } from './model';
import { fromGitUri, toGitUri } from './uri';
import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model';
import { filterEvent, eventToPromise } from './util';

interface CacheRow {
Expand All @@ -25,8 +25,8 @@ const FIVE_MINUTES = 1000 * 60 * 5;

export class GitContentProvider {

private onDidChangeEmitter = new EventEmitter<Uri>();
get onDidChange(): Event<Uri> { return this.onDidChangeEmitter.event; }
private _onDidChange = new EventEmitter<Uri>();
get onDidChange(): Event<Uri> { return this._onDidChange.event; }

private changedRepositoryRoots = new Set<string>();
private cache: Cache = Object.create(null);
Expand All @@ -35,6 +35,7 @@ export class GitContentProvider {
constructor(private model: Model) {
this.disposables.push(
model.onDidChangeRepository(this.onDidChangeRepository, this),
model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this),
workspace.registerTextDocumentContentProvider('git', this)
);

Expand All @@ -46,6 +47,14 @@ export class GitContentProvider {
this.eventuallyFireChangeEvents();
}

private onDidChangeOriginalResource({ uri }: OriginalResourceChangeEvent): void {
if (uri.scheme !== 'file') {
return;
}

this._onDidChange.fire(toGitUri(uri, '', true));
}

@debounce(1100)
private eventuallyFireChangeEvents(): void {
this.fireChangeEvents();
Expand All @@ -64,7 +73,7 @@ export class GitContentProvider {

for (const root of this.changedRepositoryRoots) {
if (fsPath.startsWith(root)) {
this.onDidChangeEmitter.fire(uri);
this._onDidChange.fire(uri);
return;
}
}
Expand Down
12 changes: 12 additions & 0 deletions extensions/git/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export interface ModelChangeEvent {
uri: Uri;
}

export interface OriginalResourceChangeEvent {
repository: Repository;
uri: Uri;
}

interface OpenRepository extends Disposable {
repository: Repository;
}
Expand All @@ -54,6 +59,9 @@ export class Model {
private _onDidChangeRepository = new EventEmitter<ModelChangeEvent>();
readonly onDidChangeRepository: Event<ModelChangeEvent> = this._onDidChangeRepository.event;

private _onDidChangeOriginalResource = new EventEmitter<OriginalResourceChangeEvent>();
readonly onDidChangeOriginalResource: Event<OriginalResourceChangeEvent> = this._onDidChangeOriginalResource.event;

private openRepositories: OpenRepository[] = [];
get repositories(): Repository[] { return this.openRepositories.map(r => r.repository); }

Expand Down Expand Up @@ -217,10 +225,14 @@ export class Model {
const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed);
const disappearListener = onDidDisappearRepository(() => dispose());
const changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri }));
const originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri }));

const dispose = () => {
disappearListener.dispose();
changeListener.dispose();
originalResourceChangeListener.dispose();
repository.dispose();

this.openRepositories = this.openRepositories.filter(e => e !== openRepository);
this._onDidCloseRepository.fire(repository);
};
Expand Down
4 changes: 4 additions & 0 deletions extensions/git/src/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@ export class Repository implements Disposable {
private _onDidChangeStatus = new EventEmitter<void>();
readonly onDidChangeStatus: Event<void> = this._onDidChangeStatus.event;

private _onDidChangeOriginalResource = new EventEmitter<Uri>();
readonly onDidChangeOriginalResource: Event<Uri> = this._onDidChangeOriginalResource.event;

private _onRunOperation = new EventEmitter<Operation>();
readonly onRunOperation: Event<Operation> = this._onRunOperation.event;

Expand Down Expand Up @@ -460,6 +463,7 @@ export class Repository implements Disposable {
async stage(resource: Uri, contents: string): Promise<void> {
const relativePath = path.relative(this.repository.root, resource.fsPath).replace(/\\/g, '/');
await this.run(Operation.Stage, () => this.repository.stage(relativePath, contents));
this._onDidChangeOriginalResource.fire(resource);
}

async revert(resources: Uri[]): Promise<void> {
Expand Down
4 changes: 4 additions & 0 deletions src/vs/base/browser/ui/actionbar/actionbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
display: inline-block;
}

.monaco-action-bar.reverse .actions-container {
flex-direction: row-reverse;
}

.monaco-action-bar .action-item {
cursor: pointer;
display: inline-block;
Expand Down
36 changes: 29 additions & 7 deletions src/vs/base/browser/ui/actionbar/actionbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,10 @@ export class ActionItem extends BaseActionItem {
}

export enum ActionsOrientation {
HORIZONTAL = 1,
VERTICAL = 2
HORIZONTAL,
HORIZONTAL_REVERSE,
VERTICAL,
VERTICAL_REVERSE,
}

export interface IActionItemProvider {
Expand Down Expand Up @@ -420,18 +422,38 @@ export class ActionBar extends EventEmitter implements IActionRunner {
DOM.addClass(this.domNode, 'animated');
}

let isVertical = this.options.orientation === ActionsOrientation.VERTICAL;
if (isVertical) {
this.domNode.className += ' vertical';
let previousKey: KeyCode;
let nextKey: KeyCode;

switch (this.options.orientation) {
case ActionsOrientation.HORIZONTAL:
previousKey = KeyCode.LeftArrow;
nextKey = KeyCode.RightArrow;
break;
case ActionsOrientation.HORIZONTAL_REVERSE:
previousKey = KeyCode.RightArrow;
nextKey = KeyCode.LeftArrow;
this.domNode.className += ' reverse';
break;
case ActionsOrientation.VERTICAL:
previousKey = KeyCode.UpArrow;
nextKey = KeyCode.DownArrow;
this.domNode.className += ' vertical';
break;
case ActionsOrientation.VERTICAL_REVERSE:
previousKey = KeyCode.DownArrow;
nextKey = KeyCode.UpArrow;
this.domNode.className += ' vertical reverse';
break;
}

$(this.domNode).on(DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e);
let eventHandled = true;

if (event.equals(isVertical ? KeyCode.UpArrow : KeyCode.LeftArrow)) {
if (event.equals(previousKey)) {
this.focusPrevious();
} else if (event.equals(isVertical ? KeyCode.DownArrow : KeyCode.RightArrow)) {
} else if (event.equals(nextKey)) {
this.focusNext();
} else if (event.equals(KeyCode.Escape)) {
this.cancel();
Expand Down
4 changes: 4 additions & 0 deletions src/vs/base/common/numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@ export function countToArray(fromOrTo: number, to?: number): number[] {

export function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}

export function rot(index: number, modulo: number): number {
return (modulo + (index % modulo)) % modulo;
}
Loading

0 comments on commit 3070c0b

Please sign in to comment.