diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 91384099d8f2233336e179c7705139a07bb03653..5a918d25fb778d0110f7cd8297c0ebf55d953dab 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -24,14 +24,13 @@ const localize = nls.loadMessageBundle(); class CheckoutItem implements QuickPickItem { protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } - protected get treeish(): string | undefined { return this.ref.name; } get label(): string { return this.ref.name || this.shortCommit; } get description(): string { return this.shortCommit; } constructor(protected ref: Ref) { } async run(repository: Repository): Promise { - const ref = this.treeish; + const ref = this.ref.name; if (!ref) { return; @@ -54,13 +53,12 @@ class CheckoutRemoteHeadItem extends CheckoutItem { return localize('remote branch at', "Remote branch at {0}", this.shortCommit); } - protected get treeish(): string | undefined { + async run(repository: Repository): Promise { if (!this.ref.name) { return; } - const match = /^[^/]+\/(.*)$/.exec(this.ref.name); - return match ? match[1] : this.ref.name; + await repository.checkoutTracking(this.ref.name); } } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 3069b8fd77b7bd509756f27bbde3c5cfde9b32c5..d678cd3b03d960e9fd3d4ee921f43387c25054f7 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -913,9 +913,13 @@ export class Repository { await this.run(['update-index', '--cacheinfo', mode, hash, path]); } - async checkout(treeish: string, paths: string[]): Promise { + async checkout(treeish: string, paths: string[], opts: { track?: boolean } = Object.create(null)): Promise { const args = ['checkout', '-q']; + if (opts.track) { + args.push('--track'); + } + if (treeish) { args.push(treeish); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 3e9134d59840dc341d30a081b4970f4e8de97930..45c139c1e21713eeb5f87fad030448da87c2d6ad 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -289,6 +289,7 @@ export const enum Operation { SetBranchUpstream = 'SetBranchUpstream', HashObject = 'HashObject', Checkout = 'Checkout', + CheckoutTracking = 'CheckoutTracking', Reset = 'Reset', Remote = 'Remote', Fetch = 'Fetch', @@ -877,6 +878,10 @@ export class Repository implements Disposable { await this.run(Operation.Checkout, () => this.repository.checkout(treeish, [])); } + async checkoutTracking(treeish: string): Promise { + await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { track: true })); + } + async getCommit(ref: string): Promise { return await this.repository.getCommit(ref); } diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index 9cc8a4f3b618437b93b93f5e0402db125ce0d2fd..fd44931846a89ce1900da7f638da27f0976eb047 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -11,26 +11,35 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { TPromise } from 'vs/base/common/winjs.base'; +import { Disposable } from 'vs/base/common/lifecycle'; -export abstract class AbstractCodeEditorService implements ICodeEditorService { +export abstract class AbstractCodeEditorService extends Disposable implements ICodeEditorService { _serviceBrand: any; - private readonly _onCodeEditorAdd: Emitter; - private readonly _onCodeEditorRemove: Emitter; - private _codeEditors: { [editorId: string]: ICodeEditor; }; + private readonly _onCodeEditorAdd: Emitter = this._register(new Emitter()); + public readonly onCodeEditorAdd: Event = this._onCodeEditorAdd.event; + + private readonly _onCodeEditorRemove: Emitter = this._register(new Emitter()); + public readonly onCodeEditorRemove: Event = this._onCodeEditorRemove.event; + + private readonly _onDiffEditorAdd: Emitter = this._register(new Emitter()); + public readonly onDiffEditorAdd: Event = this._onDiffEditorAdd.event; + + private readonly _onDiffEditorRemove: Emitter = this._register(new Emitter()); + public readonly onDiffEditorRemove: Event = this._onDiffEditorRemove.event; - private readonly _onDiffEditorAdd: Emitter; - private readonly _onDiffEditorRemove: Emitter; + private readonly _onDidChangeTransientModelProperty: Emitter = this._register(new Emitter()); + public readonly onDidChangeTransientModelProperty: Event = this._onDidChangeTransientModelProperty.event; + + + private _codeEditors: { [editorId: string]: ICodeEditor; }; private _diffEditors: { [editorId: string]: IDiffEditor; }; constructor() { + super(); this._codeEditors = Object.create(null); this._diffEditors = Object.create(null); - this._onCodeEditorAdd = new Emitter(); - this._onCodeEditorRemove = new Emitter(); - this._onDiffEditorAdd = new Emitter(); - this._onDiffEditorRemove = new Emitter(); } addCodeEditor(editor: ICodeEditor): void { @@ -38,20 +47,12 @@ export abstract class AbstractCodeEditorService implements ICodeEditorService { this._onCodeEditorAdd.fire(editor); } - get onCodeEditorAdd(): Event { - return this._onCodeEditorAdd.event; - } - removeCodeEditor(editor: ICodeEditor): void { if (delete this._codeEditors[editor.getId()]) { this._onCodeEditorRemove.fire(editor); } } - get onCodeEditorRemove(): Event { - return this._onCodeEditorRemove.event; - } - listCodeEditors(): ICodeEditor[] { return Object.keys(this._codeEditors).map(id => this._codeEditors[id]); } @@ -61,20 +62,12 @@ export abstract class AbstractCodeEditorService implements ICodeEditorService { this._onDiffEditorAdd.fire(editor); } - get onDiffEditorAdd(): Event { - return this._onDiffEditorAdd.event; - } - removeDiffEditor(editor: IDiffEditor): void { if (delete this._diffEditors[editor.getId()]) { this._onDiffEditorRemove.fire(editor); } } - get onDiffEditorRemove(): Event { - return this._onDiffEditorRemove.event; - } - listDiffEditors(): IDiffEditor[] { return Object.keys(this._diffEditors).map(id => this._diffEditors[id]); } @@ -117,6 +110,7 @@ export abstract class AbstractCodeEditorService implements ICodeEditorService { } w.set(key, value); + this._onDidChangeTransientModelProperty.fire(model); } public getTransientModelProperty(model: ITextModel, key: string): any { diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index 26dbbe2aab87a2a9d93f0d605fb77649597906bc..714ced26dab09d763cd084202bd01c6dda4dec29 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -17,11 +17,14 @@ export const ICodeEditorService = createDecorator('codeEdito export interface ICodeEditorService { _serviceBrand: any; - onCodeEditorAdd: Event; - onCodeEditorRemove: Event; + readonly onCodeEditorAdd: Event; + readonly onCodeEditorRemove: Event; + + readonly onDiffEditorAdd: Event; + readonly onDiffEditorRemove: Event; + + readonly onDidChangeTransientModelProperty: Event; - onDiffEditorAdd: Event; - onDiffEditorRemove: Event; addCodeEditor(editor: ICodeEditor): void; removeCodeEditor(editor: ICodeEditor): void; diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index c839cffd343d30d5d0bcaf38e956939fd254b752..b10d128931db31baa8efb75fbd3e56243df02a50 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -314,19 +314,31 @@ export class Transform extends Marker { resolve(value: string): string { const _this = this; - return value.replace(this.regexp, function () { - let ret = ''; - for (const marker of _this._children) { - if (marker instanceof FormatString) { - let value = arguments.length - 2 > marker.index ? arguments[marker.index] : ''; - value = marker.resolve(value); - ret += value; - } else { - ret += marker.toString(); - } - } - return ret; + let didMatch = false; + let ret = value.replace(this.regexp, function () { + didMatch = true; + return _this._replace(Array.prototype.slice.call(arguments, 0, -2)); }); + // when the regex didn't match and when the transform has + // else branches, then run those + if (!didMatch && this._children.some(child => child instanceof FormatString && Boolean(child.elseValue))) { + ret = this._replace([]); + } + return ret; + } + + private _replace(groups: string[]): string { + let ret = ''; + for (const marker of this._children) { + if (marker instanceof FormatString) { + let value = groups[marker.index] || ''; + value = marker.resolve(value); + ret += value; + } else { + ret += marker.toString(); + } + } + return ret; } toString(): string { @@ -784,9 +796,10 @@ export class SnippetParser { } let value: string; if (value = this._accept(TokenType.Backslash, true)) { - // \, or \| + // \, \|, or \\ value = this._accept(TokenType.Comma, true) || this._accept(TokenType.Pipe, true) + || this._accept(TokenType.Backslash, true) || value; } else { value = this._accept(undefined, true); diff --git a/src/vs/editor/contrib/snippet/test/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/snippetParser.test.ts index 301c9dd1b81ca40db82a9c7ddb76172128be49f0..fb67a5b263119bb5d142a06410e2876a95272642 100644 --- a/src/vs/editor/contrib/snippet/test/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetParser.test.ts @@ -353,6 +353,12 @@ suite('SnippetParser', () => { assertText('${1||}', '${1||}'); }); + test('Backslash character escape in choice tabstop doesn\'t work #58494', function () { + + const { placeholders } = new SnippetParser().parse('${1|\\,,},$,\\|,\\\\|}'); + assert.equal(placeholders.length, 1); + assert.ok(placeholders[0].choice instanceof Choice); + }); test('Parser, only textmate', () => { const p = new SnippetParser(); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 77715fd2e6deb014a8e7958f804056a9dc2f617c..151855bd0bd12f02403f4d33e050d88911b6e158 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -159,7 +159,8 @@ suite('Snippet Variables Resolver', function () { assertVariableResolve2('${foobarfoobar/(foo)/${1:+FAR}/g}', 'FARbarFARbar'); // global assertVariableResolve2('${foobarfoobar/(foo)/${1:+FAR}/}', 'FARbarfoobar'); // first match - assertVariableResolve2('${foobarfoobar/(bazz)/${1:+FAR}/g}', 'foobarfoobar'); // no match + assertVariableResolve2('${foobarfoobar/(bazz)/${1:+FAR}/g}', 'foobarfoobar'); // no match, no else + // assertVariableResolve2('${foobarfoobar/(bazz)/${1:+FAR}/g}', ''); // no match assertVariableResolve2('${foobarfoobar/(foo)/${2:+FAR}/g}', 'barbar'); // bad group reference }); @@ -292,4 +293,14 @@ suite('Snippet Variables Resolver', function () { assertVariableResolve3(resolver, 'CURRENT_MONTH_NAME'); assertVariableResolve3(resolver, 'CURRENT_MONTH_NAME_SHORT'); }); + + test('creating snippet - format-condition doesn\'t work #53617', function () { + + const snippet = new SnippetParser().parse('${TM_LINE_NUMBER/(10)/${1:?It is:It is not}/} line 10', true); + snippet.resolveVariables({ resolve() { return '10'; } }); + assert.equal(snippet.toString(), 'It is line 10'); + + snippet.resolveVariables({ resolve() { return '11'; } }); + assert.equal(snippet.toString(), 'It is not line 10'); + }); }); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts index bea4e2e3faa4b7f80eabd19113771a747030581f..e5dad3489de9680be5562e03d0516a069fcaacdb 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts @@ -114,23 +114,6 @@ function toggleWordWrap(editor: ICodeEditor, state: IWordWrapState): IWordWrapSt }; } -function applyWordWrapState(editor: ICodeEditor, state: IWordWrapState): void { - if (state.transientState) { - // toggle is on - editor.updateOptions({ - wordWrap: state.transientState.forceWordWrap, - wordWrapMinified: state.transientState.forceWordWrapMinified - }); - return; - } - - // toggle is off - editor.updateOptions({ - wordWrap: state.configuredWordWrap, - wordWrapMinified: state.configuredWordWrapMinified - }); -} - const TOGGLE_WORD_WRAP_ID = 'editor.action.toggleWordWrap'; class ToggleWordWrapAction extends EditorAction { @@ -170,9 +153,8 @@ class ToggleWordWrapAction extends EditorAction { // Compute the new state const newState = toggleWordWrap(editor, currentState); // Write the new state + // (this will cause an event and the controller will apply the state) writeTransientState(model, newState.transientState, codeEditorService); - // Apply the new state - applyWordWrapState(editor, newState); } } @@ -192,6 +174,7 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution const isWordWrapMinified = this.contextKeyService.createKey(isWordWrapMinifiedKey, this._isWordWrapMinified(configuration)); const isDominatedByLongLines = this.contextKeyService.createKey(isDominatedByLongLinesKey, this._isDominatedByLongLines(configuration)); const inDiffEditor = this.contextKeyService.createKey(inDiffEditorKey, this._inDiffEditor(configuration)); + let currentlyApplyingEditorConfig = false; this._register(editor.onDidChangeConfiguration((e) => { if (!e.wrappingInfo) { @@ -201,9 +184,21 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution isWordWrapMinified.set(this._isWordWrapMinified(configuration)); isDominatedByLongLines.set(this._isDominatedByLongLines(configuration)); inDiffEditor.set(this._inDiffEditor(configuration)); + if (!currentlyApplyingEditorConfig) { + // I am not the cause of the word wrap getting changed + ensureWordWrapSettings(); + } })); this._register(editor.onDidChangeModel((e) => { + ensureWordWrapSettings(); + })); + + this._register(codeEditorService.onDidChangeTransientModelProperty(() => { + ensureWordWrapSettings(); + })); + + const ensureWordWrapSettings = () => { // Ensure correct word wrap settings const newModel = this.editor.getModel(); if (!newModel) { @@ -223,8 +218,30 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution const desiredState = readWordWrapState(newModel, this.configurationService, this.codeEditorService); // Apply the state - applyWordWrapState(editor, desiredState); - })); + try { + currentlyApplyingEditorConfig = true; + this._applyWordWrapState(desiredState); + } finally { + currentlyApplyingEditorConfig = false; + } + }; + } + + private _applyWordWrapState(state: IWordWrapState): void { + if (state.transientState) { + // toggle is on + this.editor.updateOptions({ + wordWrap: state.transientState.forceWordWrap, + wordWrapMinified: state.transientState.forceWordWrapMinified + }); + return; + } + + // toggle is off + this.editor.updateOptions({ + wordWrap: state.configuredWordWrap, + wordWrapMinified: state.configuredWordWrapMinified + }); } private _isWordWrapMinified(config: InternalEditorOptions): boolean { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 0a98d5655b7890acef6dfbdc2b7a0ed2617ace9e..2b5f18719b34b36959f2defc1a027fa4d1111f3d 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -947,6 +947,7 @@ export class TestCodeEditorService implements ICodeEditorService { onCodeEditorRemove: Event = Event.None; onDiffEditorAdd: Event = Event.None; onDiffEditorRemove: Event = Event.None; + onDidChangeTransientModelProperty: Event = Event.None; addCodeEditor(editor: ICodeEditor): void { } removeCodeEditor(editor: ICodeEditor): void { }