From c9fde20123c8c505727c2bfc35b5784ba9243f63 Mon Sep 17 00:00:00 2001 From: fetiew Date: Mon, 16 Apr 2018 23:51:40 -0400 Subject: [PATCH 001/149] Implement word part move and delete --- src/vs/base/common/strings.ts | 24 ++ .../common/controller/cursorWordOperations.ts | 128 ++++++++++- .../test/wordPartOperations.test.ts | 207 ++++++++++++++++++ .../wordPartOperations/wordPartOperations.ts | 113 ++++++++++ src/vs/editor/editor.all.ts | 1 + 5 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts create mode 100644 src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index deeb3ae1f999d..f78b437eb5355 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -323,6 +323,30 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len return -1; } +export function lastWordPartEnd(str: string, startIndex: number = str.length-1): number{ + for (let i = startIndex; i >= 0; i--) { + let chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { + return i-1; + } + } + return -1; +} + +export function nextWordPartBegin(str: string, startIndex: number = str.length-1): number{ + const checkLowerCase = str.charCodeAt(startIndex-1) === CharCode.Space; // does a lc char count as a part start? + for (let i = startIndex; i < str.length; ++i){ + let chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || (checkLowerCase && isLowerAsciiLetter(chCode))) { + return i+1; + } + if (chCode === CharCode.Underline){ + return i+2; + } + } + return -1; +} + export function compare(a: string, b: string): number { if (a < b) { return -1; diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 24bbcac19c3d9..a0410aaf74c28 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -229,7 +229,7 @@ export class WordOperations { return new Position(lineNumber, column); } - private static _deleteWordLeftWhitespace(model: ICursorSimpleModel, position: Position): Range { + protected static _deleteWordLeftWhitespace(model: ICursorSimpleModel, position: Position): Range { const lineContent = model.getLineContent(position.lineNumber); const startIndex = position.column - 2; const lastNonWhitespace = strings.lastNonWhitespaceIndex(lineContent, startIndex); @@ -304,7 +304,7 @@ export class WordOperations { return len; } - private static _deleteWordRightWhitespace(model: ICursorSimpleModel, position: Position): Range { + protected static _deleteWordRightWhitespace(model: ICursorSimpleModel, position: Position): Range { const lineContent = model.getLineContent(position.lineNumber); const startIndex = position.column - 1; const firstNonWhitespace = this._findFirstNonWhitespaceChar(lineContent, startIndex); @@ -454,3 +454,127 @@ export class WordOperations { return cursor.move(true, lineNumber, column, 0); } } + +export class WordPartOperations extends WordOperations { + public static deleteWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + if (!selection.isEmpty()) { + return selection; + } + + const position = new Position(selection.positionLineNumber, selection.positionColumn); + + let lineNumber = position.lineNumber; + let column = position.column; + + if (lineNumber === 1 && column === 1) { + // Ignore deleting at beginning of file + return null; + } + + if (whitespaceHeuristics) { + let r = WordOperations._deleteWordLeftWhitespace(model, position); + if (r) { + return r; + } + } + + const lineContent = model.getLineContent(position.lineNumber); + const startIndex = position.column - 2; + const lastWordPartEnd = strings.lastWordPartEnd(lineContent, startIndex); + const wordRange = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + + if (lastWordPartEnd === -1 || (wordRange.startColumn > lastWordPartEnd && wordRange.startColumn < column)){ + return wordRange; + } + else { + const range = new Range(lineNumber, column, lineNumber, lastWordPartEnd+2); + return range; + } + + } + + public static deleteWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + if (!selection.isEmpty()) { + return selection; + } + + const position = new Position(selection.positionLineNumber, selection.positionColumn); + + let lineNumber = position.lineNumber; + let column = position.column; + + const lineCount = model.getLineCount(); + const maxColumn = model.getLineMaxColumn(lineNumber); + if (lineNumber === lineCount && column === maxColumn) { + // Ignore deleting at end of file + return null; + } + + if (whitespaceHeuristics) { + let r = WordOperations._deleteWordRightWhitespace(model, position); + if (r) { + return r; + } + } + + const lineContent = model.getLineContent(position.lineNumber); + const startIndex = position.column; + const nextWordPartBegin = strings.nextWordPartBegin(lineContent, startIndex); + const wordRange = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + + if (nextWordPartBegin === -1 || (wordRange && wordRange.endColumn < nextWordPartBegin && wordRange.endColumn >= column)){ + return wordRange; + } + else { + return new Range(lineNumber, column, lineNumber, nextWordPartBegin); + } + } + + public static moveWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { + const startIndex = position.column - 2; + const lineNumber = position.lineNumber; + const column = position.column; + const wordPartCol = strings.lastWordPartEnd(model.getLineContent(lineNumber), startIndex); + const wordPos = WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType); + + if (column === 1) { + if (lineNumber > 1) { + return new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber-1)); + } + return null; + } + + if (wordPartCol === -1 || (wordPos.column > wordPartCol && wordPos.column < column && wordPos.lineNumber === lineNumber)){ + return wordPos; + } + else{ + return new Position(lineNumber, wordPartCol+2); + } + } + + public static moveWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { + const startIndex = position.column; + const lineNumber = position.lineNumber; + const column = position.column; + const wordPartCol = strings.nextWordPartBegin(model.getLineContent(lineNumber), startIndex); + const wordPos = WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType); + + const lineCount = model.getLineCount(); + const maxColumn = model.getLineMaxColumn(lineNumber); + if (column === maxColumn) { + if (lineNumber < lineCount) { + return new Position(lineNumber + 1, 1); + } + return null; + } + + + if (wordPartCol === -1 || (wordPos.column < wordPartCol && wordPos.column > column && wordPos.lineNumber === lineNumber)){ + return wordPos; + } + else { + return new Position(lineNumber, wordPartCol); + } + } + +} diff --git a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts new file mode 100644 index 0000000000000..ac44bfe79bb06 --- /dev/null +++ b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts @@ -0,0 +1,207 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as assert from 'assert'; +import { Position } from 'vs/editor/common/core/position'; +import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { + DeleteWordPartLeft, DeleteWordPartRight, + CursorWordPartLeft, CursorWordPartRight +} from 'vs/editor/contrib/wordPartOperations/wordPartOperations'; +import { EditorCommand } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; + +suite('WordPartOperations', () => { + const _deleteWordPartLeft = new DeleteWordPartLeft(); + const _deleteWordPartRight = new DeleteWordPartRight(); + const _cursorWordPartLeft = new CursorWordPartLeft(); + const _cursorWordPartRight = new CursorWordPartRight(); + + function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void { + command.runEditorCommand(null, editor, null); + } + function moveWordPartLeft(editor: ICodeEditor, inSelectionmode: boolean = false): void{ + runEditorCommand(editor, inSelectionmode ? _cursorWordPartLeft : _cursorWordPartLeft); + } + function moveWordPartRight(editor: ICodeEditor, inSelectionmode: boolean = false): void{ + runEditorCommand(editor, inSelectionmode ? _cursorWordPartLeft : _cursorWordPartRight); + } + function deleteWordPartLeft(editor: ICodeEditor): void{ + runEditorCommand(editor, _deleteWordPartLeft); + } + function deleteWordPartRight(editor: ICodeEditor): void{ + runEditorCommand(editor, _deleteWordPartRight); + } + + test('move word part left basic', () => { + withTestCodeEditor([ + 'start line', + 'thisIsACamelCaseVar this_is_a_snake_case_var', + 'end line' + ], {}, (editor, _) => { + editor.setPosition(new Position(3, 8)); + const expectedStops = [ + [3, 5], + [3, 4], + [3, 1], + [2, 46], + [2, 42], + [2, 37], + [2, 31], + [2, 29], + [2, 26], + [2, 22], + [2, 21], + [2, 20], + [2, 17], + [2, 13], + [2, 8], + [2, 7], + [2, 5], + [2, 1], + [1, 11], + [1, 7], + [1, 6], + [1, 1] + ]; + + let actualStops: number[][] = []; + for (let i = 0; i < expectedStops.length; i++) { + moveWordPartLeft(editor); + const pos = editor.getPosition(); + actualStops.push([pos.lineNumber, pos.column]); + } + + assert.deepEqual(actualStops, expectedStops); + }); + }); + + test('move word part right basic', () => { + withTestCodeEditor([ + 'start line', + 'thisIsACamelCaseVar this_is_a_snake_case_var', + 'end line' + ], {}, (editor, _) => { + editor.setPosition(new Position(1, 1)); + const expectedStops = [ + [1, 6], + [1, 7], + [1, 11], + [2, 1], + [2, 5], + [2, 7], + [2, 8], + [2, 13], + [2, 17], + [2, 20], + [2, 21], + [2, 22], + [2, 27], + [2, 30], + [2, 32], + [2, 38], + [2, 43], + [2, 46], + [3, 1], + [3, 4], + [3, 5], + [3, 9] + ]; + + let actualStops: number[][] = []; + for (let i = 0; i < expectedStops.length; i++) { + moveWordPartRight(editor); + const pos = editor.getPosition(); + actualStops.push([pos.lineNumber, pos.column]); + } + + assert.deepEqual(actualStops, expectedStops); + }); + }); + + test('delete word part left basic', () => { + withTestCodeEditor([ + ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 84)); + + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case', '001'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake', '002'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a', '003'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is', '004'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this', '005'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar ', '006'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar', '007'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCase', '008'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamel', '009'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsA', '010'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIs', '011'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ this', '012'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ ', '013'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */', '014'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 ', '015'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-', '016'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5', '017'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '018'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '019'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '020'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+=', '021'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '022'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text ', '023'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text', '024'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some ', '025'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some', '026'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just ', '027'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just', '028'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* ', '029'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /*', '030'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' ', '031'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), '', '032'); + }); + }); + + test('delete word part right basic', () => { + withTestCodeEditor([ + ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var' + ], {}, (editor, _) => { + const model = editor.getModel(); + editor.setPosition(new Position(1, 1)); + + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '/* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '001'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '002'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '003'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '004'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '005'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '006'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '007'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '008'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '009'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '010'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '011'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '012'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '013'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '014'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '015'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' */ thisIsACamelCaseVar this_is_a_snake_case_var', '016'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' thisIsACamelCaseVar this_is_a_snake_case_var', '017'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'thisIsACamelCaseVar this_is_a_snake_case_var', '018'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'IsACamelCaseVar this_is_a_snake_case_var', '019'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'ACamelCaseVar this_is_a_snake_case_var', '020'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CamelCaseVar this_is_a_snake_case_var', '021'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CaseVar this_is_a_snake_case_var', '022'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Var this_is_a_snake_case_var', '023'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' this_is_a_snake_case_var', '024'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'this_is_a_snake_case_var', '025'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'is_a_snake_case_var', '026'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'a_snake_case_var', '027'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'snake_case_var', '028'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'case_var', '029'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'var', '030'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '', '031'); + }); + }); +}); diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts new file mode 100644 index 0000000000000..781d1ed588e8b --- /dev/null +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ITextModel } from 'vs/editor/common/model'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Selection } from 'vs/editor/common/core/selection'; +import { registerEditorCommand } from 'vs/editor/browser/editorExtensions'; +import { Range } from 'vs/editor/common/core/range'; +import { WordNavigationType, WordPartOperations } from 'vs/editor/common/controller/cursorWordOperations'; +import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { DeleteWordCommand, MoveWordCommand } from '../wordOperations/wordOperations'; +import { Position } from 'vs/editor/common/core/position'; + +export class DeleteWordPartLeft extends DeleteWordCommand { + constructor() { + super({ + whitespaceHeuristics: true, + wordNavigationType: WordNavigationType.WordStart, + id: 'deleteWordPartLeft', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyCode.Backspace, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } + } + }); + } + + protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + let r = WordPartOperations.deleteWordPartLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + if (r) { + return r; + } + return new Range(1, 1, 1, 1); + } +} + +export class DeleteWordPartRight extends DeleteWordCommand { + constructor() { + super({ + whitespaceHeuristics: true, + wordNavigationType: WordNavigationType.WordEnd, + id: 'deleteWordPartRight', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyCode.Delete, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Delete } + } + }); + } + + protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { + let r = WordPartOperations.deleteWordPartRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + if (r) { + return r; + } + const lineCount = model.getLineCount(); + const maxColumn = model.getLineMaxColumn(lineCount); + return new Range(lineCount, maxColumn, lineCount, maxColumn); + } +} + +export class CursorWordPartLeft extends MoveWordCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordPartStartLeft', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyCode.LeftArrow, + mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } + } + }); + } + + protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return WordPartOperations.moveWordPartLeft(wordSeparators, model, position, wordNavigationType); + } +} + +export class CursorWordPartRight extends MoveWordCommand { + constructor() { + super({ + inSelectionMode: false, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordPartRight', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyCode.RightArrow, + mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } + } + }); + } + + protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return WordPartOperations.moveWordPartRight(wordSeparators, model, position, wordNavigationType); + } +} + + +registerEditorCommand(new DeleteWordPartLeft()); +registerEditorCommand(new DeleteWordPartRight()); +registerEditorCommand(new CursorWordPartLeft()); +registerEditorCommand(new CursorWordPartRight()); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 07cbf332285eb..edcf0f3b6d64c 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -41,3 +41,4 @@ import 'vs/editor/contrib/suggest/suggestController'; import 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; import 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import 'vs/editor/contrib/wordOperations/wordOperations'; +import 'vs/editor/contrib/wordPartOperations/wordPartOperations'; From 7415670044d2a0128c6d232a19192b2edcd3a422 Mon Sep 17 00:00:00 2001 From: fetiew Date: Tue, 17 Apr 2018 17:12:50 -0400 Subject: [PATCH 002/149] Fixed formatting issues --- src/vs/base/common/strings.ts | 16 ++++++++-------- .../common/controller/cursorWordOperations.ts | 16 ++++++++-------- .../test/wordPartOperations.test.ts | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index f78b437eb5355..d423e60460932 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -323,25 +323,25 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len return -1; } -export function lastWordPartEnd(str: string, startIndex: number = str.length-1): number{ +export function lastWordPartEnd(str: string, startIndex: number = str.length - 1): number { for (let i = startIndex; i >= 0; i--) { let chCode = str.charCodeAt(i); if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { - return i-1; + return i - 1; } } return -1; } -export function nextWordPartBegin(str: string, startIndex: number = str.length-1): number{ - const checkLowerCase = str.charCodeAt(startIndex-1) === CharCode.Space; // does a lc char count as a part start? - for (let i = startIndex; i < str.length; ++i){ +export function nextWordPartBegin(str: string, startIndex: number = str.length - 1): number { + const checkLowerCase = str.charCodeAt(startIndex - 1) === CharCode.Space; // does a lc char count as a part start? + for (let i = startIndex; i < str.length; ++i) { let chCode = str.charCodeAt(i); if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || (checkLowerCase && isLowerAsciiLetter(chCode))) { - return i+1; + return i + 1; } - if (chCode === CharCode.Underline){ - return i+2; + if (chCode === CharCode.Underline) { + return i + 2; } } return -1; diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index a0410aaf74c28..1f620a7637873 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -483,11 +483,11 @@ export class WordPartOperations extends WordOperations { const lastWordPartEnd = strings.lastWordPartEnd(lineContent, startIndex); const wordRange = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); - if (lastWordPartEnd === -1 || (wordRange.startColumn > lastWordPartEnd && wordRange.startColumn < column)){ + if (lastWordPartEnd === -1 || (wordRange.startColumn > lastWordPartEnd && wordRange.startColumn < column)) { return wordRange; } else { - const range = new Range(lineNumber, column, lineNumber, lastWordPartEnd+2); + const range = new Range(lineNumber, column, lineNumber, lastWordPartEnd + 2); return range; } @@ -522,7 +522,7 @@ export class WordPartOperations extends WordOperations { const nextWordPartBegin = strings.nextWordPartBegin(lineContent, startIndex); const wordRange = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); - if (nextWordPartBegin === -1 || (wordRange && wordRange.endColumn < nextWordPartBegin && wordRange.endColumn >= column)){ + if (nextWordPartBegin === -1 || (wordRange && wordRange.endColumn < nextWordPartBegin && wordRange.endColumn >= column)) { return wordRange; } else { @@ -539,16 +539,16 @@ export class WordPartOperations extends WordOperations { if (column === 1) { if (lineNumber > 1) { - return new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber-1)); + return new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); } return null; } - if (wordPartCol === -1 || (wordPos.column > wordPartCol && wordPos.column < column && wordPos.lineNumber === lineNumber)){ + if (wordPartCol === -1 || (wordPos.column > wordPartCol && wordPos.column < column && wordPos.lineNumber === lineNumber)) { return wordPos; } - else{ - return new Position(lineNumber, wordPartCol+2); + else { + return new Position(lineNumber, wordPartCol + 2); } } @@ -569,7 +569,7 @@ export class WordPartOperations extends WordOperations { } - if (wordPartCol === -1 || (wordPos.column < wordPartCol && wordPos.column > column && wordPos.lineNumber === lineNumber)){ + if (wordPartCol === -1 || (wordPos.column < wordPartCol && wordPos.column > column && wordPos.lineNumber === lineNumber)) { return wordPos; } else { diff --git a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts index ac44bfe79bb06..e5fa29f253536 100644 --- a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts +++ b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts @@ -23,16 +23,16 @@ suite('WordPartOperations', () => { function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void { command.runEditorCommand(null, editor, null); } - function moveWordPartLeft(editor: ICodeEditor, inSelectionmode: boolean = false): void{ + function moveWordPartLeft(editor: ICodeEditor, inSelectionmode: boolean = false): void { runEditorCommand(editor, inSelectionmode ? _cursorWordPartLeft : _cursorWordPartLeft); } - function moveWordPartRight(editor: ICodeEditor, inSelectionmode: boolean = false): void{ + function moveWordPartRight(editor: ICodeEditor, inSelectionmode: boolean = false): void { runEditorCommand(editor, inSelectionmode ? _cursorWordPartLeft : _cursorWordPartRight); } - function deleteWordPartLeft(editor: ICodeEditor): void{ + function deleteWordPartLeft(editor: ICodeEditor): void { runEditorCommand(editor, _deleteWordPartLeft); } - function deleteWordPartRight(editor: ICodeEditor): void{ + function deleteWordPartRight(editor: ICodeEditor): void { runEditorCommand(editor, _deleteWordPartRight); } From 1ee51b187b56ba25d1ac272181accbbec8a376a8 Mon Sep 17 00:00:00 2001 From: Christoph Seitz Date: Mon, 11 Jun 2018 17:13:48 +0200 Subject: [PATCH 003/149] Add support for parsing transformations in placeholders. --- .../editor/contrib/snippet/snippetParser.ts | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index 2ee46939849ad..83b222ce07b91 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -246,6 +246,15 @@ export class Placeholder extends Marker { : undefined; } + get transform(): Transform { + for (const child of this._children) { + if (child instanceof Transform) { + return child as Transform; + } + } + return undefined; + } + toTextmateString(): string { if (this.children.length === 0) { return `\$${this.index}`; @@ -482,6 +491,9 @@ export class TextmateSnippet extends Marker { let ret = 0; walk([marker], marker => { ret += marker.len(); + if (marker instanceof Transform) { + return false; + } return true; }); return ret; @@ -690,10 +702,17 @@ export class SnippetParser { // ${1:} while (true) { - // ...} -> done + if (this._accept(TokenType.CurlyClose)) { + // ...} -> done parent.appendChild(placeholder); return true; + } else if (this._accept(TokenType.Forwardslash)) { + //..///} -> transform + if (this._parseTransform(placeholder)) { + parent.appendChild(placeholder); + return true; + } } if (this._parse(placeholder)) { @@ -722,6 +741,12 @@ export class SnippetParser { placeholder.appendChild(choice); parent.appendChild(placeholder); return true; + } else if (this._accept(TokenType.Forwardslash)) { + // ...|///} -> transform + if (this._parseTransform(placeholder)) { + parent.appendChild(placeholder); + return true; + } } } @@ -729,6 +754,16 @@ export class SnippetParser { return false; } + } else if (this._accept(TokenType.Forwardslash)) { + // ${1///} + if (this._parseTransform(placeholder)) { + parent.appendChild(placeholder); + return true; + } + + this._backTo(token); + return false; + } else if (this._accept(TokenType.CurlyClose)) { // ${1} parent.appendChild(placeholder); @@ -829,7 +864,7 @@ export class SnippetParser { } } - private _parseTransform(parent: Variable): boolean { + private _parseTransform(parent: Variable | Placeholder): boolean { // ...//} let transform = new Transform(); From cea99a6f36f6ae5181f094af92e7e3b7ef543b26 Mon Sep 17 00:00:00 2001 From: Christoph Seitz Date: Mon, 11 Jun 2018 17:21:13 +0200 Subject: [PATCH 004/149] Execute placeholder transformation on moving to next placeholder. --- .../editor/contrib/snippet/snippetSession.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 31be6b2ba1662..f7966a560c901 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -87,6 +87,28 @@ export class OneSnippet { this._initDecorations(); + // Transform placeholder text if necessary + if (this._placeholderGroupsIdx >= 0) { + const firstPlaceholder = this._placeholderGroups[this._placeholderGroupsIdx][0]; + + // Check if the placeholder has a transformation + if (firstPlaceholder.transform) { + const id = this._placeholderDecorations.get(firstPlaceholder); + const range = this._editor.getModel().getDecorationRange(id); + const currentValue = this._editor.getModel().getValueInRange(range); + let operations: IIdentifiedSingleEditOperation[] = []; + + // Apply the transformed text on every placeholder occurence + for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { + const id = this._placeholderDecorations.get(placeholder); + const range = this._editor.getModel().getDecorationRange(id); + + operations.push({ range: range, text: firstPlaceholder.transform.resolve(currentValue) }); + } + this._editor.getModel().applyEdits(operations); + } + } + if (fwd === true && this._placeholderGroupsIdx < this._placeholderGroups.length - 1) { this._placeholderGroupsIdx += 1; From 6b38a7c0e661f0679402083125d4b2b5aa0653a9 Mon Sep 17 00:00:00 2001 From: Christoph Seitz Date: Fri, 15 Jun 2018 12:39:17 +0200 Subject: [PATCH 005/149] Rewrite transform handling in marker. --- .../editor/contrib/snippet/snippetParser.ts | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index 83b222ce07b91..761703117f847 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -214,8 +214,11 @@ export class Text extends Marker { } } -export class Placeholder extends Marker { +export abstract class TransformableMarker extends Marker { + public transform: Transform; +} +export class Placeholder extends TransformableMarker { static compareByIndex(a: Placeholder, b: Placeholder): number { if (a.index === b.index) { return 0; @@ -246,27 +249,27 @@ export class Placeholder extends Marker { : undefined; } - get transform(): Transform { - for (const child of this._children) { - if (child instanceof Transform) { - return child as Transform; - } - } - return undefined; - } - toTextmateString(): string { - if (this.children.length === 0) { + let transformString = ''; + if (this.transform) { + transformString = this.transform.toTextmateString(); + } + if (this.children.length === 0 && !this.transform) { return `\$${this.index}`; + } else if (this.children.length === 0) { + return `\${${this.index}${transformString}}`; } else if (this.choice) { - return `\${${this.index}|${this.choice.toTextmateString()}|}`; + return `\${${this.index}|${this.choice.toTextmateString()}|${transformString}}`; } else { - return `\${${this.index}:${this.children.map(child => child.toTextmateString()).join('')}}`; + return `\${${this.index}:${this.children.map(child => child.toTextmateString()).join('')}${transformString}}`; } } clone(): Placeholder { let ret = new Placeholder(this.index); + if (this.transform) { + ret.transform = this.transform.clone(); + } ret._children = this.children.map(child => child.clone()); return ret; } @@ -393,7 +396,7 @@ export class FormatString extends Marker { } } -export class Variable extends Marker { +export class Variable extends TransformableMarker { constructor(public name: string) { super(); @@ -401,9 +404,8 @@ export class Variable extends Marker { resolve(resolver: VariableResolver): boolean { let value = resolver.resolve(this); - let [firstChild] = this._children; - if (firstChild instanceof Transform && this._children.length === 1) { - value = firstChild.resolve(value || ''); + if (this.transform) { + value = this.transform.resolve(value || ''); } if (value !== undefined) { this._children = [new Text(value)]; @@ -413,15 +415,22 @@ export class Variable extends Marker { } toTextmateString(): string { + let transformString = ''; + if (this.transform) { + transformString = this.transform.toTextmateString(); + } if (this.children.length === 0) { - return `\${${this.name}}`; + return `\${${this.name}${transformString}}`; } else { - return `\${${this.name}:${this.children.map(child => child.toTextmateString()).join('')}}`; + return `\${${this.name}:${this.children.map(child => child.toTextmateString()).join('')}${transformString}}`; } } clone(): Variable { const ret = new Variable(this.name); + if (this.transform) { + ret.transform = this.transform.clone(); + } ret._children = this.children.map(child => child.clone()); return ret; } @@ -592,6 +601,7 @@ export class SnippetParser { for (const placeholder of incompletePlaceholders) { if (placeholderDefaultValues.has(placeholder.index)) { const clone = new Placeholder(placeholder.index); + clone.transform = placeholder.transform; for (const child of placeholderDefaultValues.get(placeholder.index)) { clone.appendChild(child.clone()); } @@ -864,7 +874,7 @@ export class SnippetParser { } } - private _parseTransform(parent: Variable | Placeholder): boolean { + private _parseTransform(parent: TransformableMarker): boolean { // ...//} let transform = new Transform(); @@ -929,7 +939,7 @@ export class SnippetParser { return false; } - parent.appendChild(transform); + parent.transform = transform; return true; } From 2727ad964bd3f55064385a2aca547c70668d06ae Mon Sep 17 00:00:00 2001 From: Christoph Seitz Date: Fri, 15 Jun 2018 12:40:56 +0200 Subject: [PATCH 006/149] Fix transformation parsing and replacement. --- .../editor/contrib/snippet/snippetParser.ts | 29 ++++++++++++------- .../editor/contrib/snippet/snippetSession.ts | 19 +++++------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index 761703117f847..93a992b3d17a0 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -712,17 +712,21 @@ export class SnippetParser { // ${1:} while (true) { - + // ...} -> done if (this._accept(TokenType.CurlyClose)) { - // ...} -> done parent.appendChild(placeholder); return true; - } else if (this._accept(TokenType.Forwardslash)) { - //..///} -> transform + } + + //..///} -> transform + if (this._accept(TokenType.Forwardslash)) { if (this._parseTransform(placeholder)) { parent.appendChild(placeholder); return true; } + + this._backTo(token); + return false; } if (this._parse(placeholder)) { @@ -746,17 +750,20 @@ export class SnippetParser { continue; } - if (this._accept(TokenType.Pipe) && this._accept(TokenType.CurlyClose)) { - // ..|} -> done + if (this._accept(TokenType.Pipe)) { placeholder.appendChild(choice); - parent.appendChild(placeholder); - return true; - } else if (this._accept(TokenType.Forwardslash)) { - // ...|///} -> transform - if (this._parseTransform(placeholder)) { + if (this._accept(TokenType.CurlyClose)) { + // ..|} -> done parent.appendChild(placeholder); return true; } + if (this._accept(TokenType.Forwardslash)) { + // ...|///} -> transform + if (this._parseTransform(placeholder)) { + parent.appendChild(placeholder); + return true; + } + } } } diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index f7966a560c901..9d9f336c9290e 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -89,24 +89,19 @@ export class OneSnippet { // Transform placeholder text if necessary if (this._placeholderGroupsIdx >= 0) { - const firstPlaceholder = this._placeholderGroups[this._placeholderGroupsIdx][0]; + let operations: IIdentifiedSingleEditOperation[] = []; - // Check if the placeholder has a transformation - if (firstPlaceholder.transform) { - const id = this._placeholderDecorations.get(firstPlaceholder); - const range = this._editor.getModel().getDecorationRange(id); - const currentValue = this._editor.getModel().getValueInRange(range); - let operations: IIdentifiedSingleEditOperation[] = []; - - // Apply the transformed text on every placeholder occurence - for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { + for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { + // Check if the placeholder has a transformation + if (placeholder.transform) { const id = this._placeholderDecorations.get(placeholder); const range = this._editor.getModel().getDecorationRange(id); + const currentValue = this._editor.getModel().getValueInRange(range); - operations.push({ range: range, text: firstPlaceholder.transform.resolve(currentValue) }); + operations.push({ range: range, text: placeholder.transform.resolve(currentValue) }); } - this._editor.getModel().applyEdits(operations); } + this._editor.getModel().applyEdits(operations); } if (fwd === true && this._placeholderGroupsIdx < this._placeholderGroups.length - 1) { From ba152e00d94ef5073af4bb0bf793ef3b3cfa5744 Mon Sep 17 00:00:00 2001 From: Christoph Seitz Date: Fri, 15 Jun 2018 12:43:21 +0200 Subject: [PATCH 007/149] Add placeholder transformation tests. --- .../snippet/test/snippetParser.test.ts | 90 +++++++++++++++++++ .../snippet/test/snippetSession.test.ts | 75 ++++++++++++++++ 2 files changed, 165 insertions(+) diff --git a/src/vs/editor/contrib/snippet/test/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/snippetParser.test.ts index 20d5d3c96af12..5cae847a72615 100644 --- a/src/vs/editor/contrib/snippet/test/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetParser.test.ts @@ -240,6 +240,35 @@ suite('SnippetParser', () => { }); + test('Parser, placeholder transforms', function () { + assertTextAndMarker('${1///}', '', Placeholder); + assertTextAndMarker('${1/regex/format/gmi}', '', Placeholder); + assertTextAndMarker('${1/([A-Z][a-z])/format/}', '', Placeholder); + + // tricky regex + assertTextAndMarker('${1/m\\/atch/$1/i}', '', Placeholder); + assertMarker('${1/regex\/format/options}', Text); + + // incomplete + assertTextAndMarker('${1///', '${1///', Text); + assertTextAndMarker('${1/regex/format/options', '${1/regex/format/options', Text); + }); + + test('Parser, placeholder with defaults and transformation', () => { + assertTextAndMarker('${1:value/foo/bar/}', 'value', Placeholder); + assertTextAndMarker('${1:bar${2:foo}bar/foo/bar/}', 'barfoobar', Placeholder); + + // incomplete + assertTextAndMarker('${1:bar${2:foobar}/foo/bar/', '${1:barfoobar/foo/bar/', Text, Placeholder, Text); + }); + + test('Parser, placeholder with choice and transformation', () => { + assertTextAndMarker('${1|one,two,three|/foo/bar/}', 'one', Placeholder); + assertTextAndMarker('${1|one|/foo/bar/}', 'one', Placeholder); + assertTextAndMarker('${1|one,two,three,|/foo/bar/}', '${1|one,two,three,|/foo/bar/}', Text); + assertTextAndMarker('${1|one,/foo/bar/', '${1|one,/foo/bar/', Text); + }); + test('No way to escape forward slash in snippet regex #36715', function () { assertMarker('${TM_DIRECTORY/src\\//$1/}', Variable); }); @@ -378,6 +407,36 @@ suite('SnippetParser', () => { assert.ok(marker[0] instanceof Variable); }); + test('Parser, transform example', () => { + let marker = new SnippetParser().parse('${1:name} : ${2:type}${3: :=/\\s:=(.*)/${1:+ :=}${1}/};\n$0'); + let childs = marker.children; + + assert.ok(childs[0] instanceof Placeholder); + assert.equal(childs[0].children.length, 1); + assert.equal(childs[0].children[0].toString(), 'name'); + assert.equal((childs[0]).transform, undefined); + assert.ok(childs[1] instanceof Text); + assert.equal(childs[1].toString(), ' : '); + assert.ok(childs[2] instanceof Placeholder); + assert.equal(childs[2].children.length, 1); + assert.equal(childs[2].children[0].toString(), 'type'); + assert.ok(childs[3] instanceof Placeholder); + assert.equal(childs[3].children.length, 1); + assert.equal(childs[3].children[0].toString(), ' :='); + assert.notEqual((childs[3]).transform, undefined); + let t = (childs[3]).transform; + assert.equal(t.regexp, '/\\s:=(.*)/'); + assert.equal(t.children.length, 2); + assert.ok(t.children[0] instanceof FormatString); + assert.equal((t.children[0]).index, 1); + assert.equal((t.children[0]).ifValue, ' :='); + assert.ok(t.children[1] instanceof FormatString); + assert.equal((t.children[1]).index, 1); + assert.ok(childs[4] instanceof Text); + assert.equal(childs[4].toString(), ';\n'); + + }); + test('Parser, default placeholder values', () => { assertMarker('errorContext: `${1:err}`, error: $1', Text, Placeholder, Text, Placeholder); @@ -393,6 +452,37 @@ suite('SnippetParser', () => { assert.equal(((p2).children[0]), 'err'); }); + test('Parser, default placeholder values and one transform', () => { + + assertMarker('errorContext: `${1:err/err/ok/}`, error: $1', Text, Placeholder, Text, Placeholder); + + const [, p1, , p2] = new SnippetParser().parse('errorContext: `${1:err/err/ok/}`, error:$1').children; + + assert.equal((p1).index, '1'); + assert.equal((p1).children.length, '1'); + assert.equal(((p1).children[0]), 'err'); + assert.notEqual((p1).transform, undefined); + + assert.equal((p2).index, '1'); + assert.equal((p2).children.length, '1'); + assert.equal(((p2).children[0]), 'err'); + assert.equal((p2).transform, undefined); + + assertMarker('errorContext: `${1:err}`, error: ${1/err/ok/}', Text, Placeholder, Text, Placeholder); + + const [, p3, , p4] = new SnippetParser().parse('errorContext: `${1:err}`, error:${1/err/ok/}').children; + + assert.equal((p3).index, '1'); + assert.equal((p3).children.length, '1'); + assert.equal(((p3).children[0]), 'err'); + assert.equal((p3).transform, undefined); + + assert.equal((p4).index, '1'); + assert.equal((p4).children.length, '1'); + assert.equal(((p4).children[0]), 'err'); + assert.notEqual((p4).transform, undefined); + }); + test('Repeated snippet placeholder should always inherit, #31040', function () { assertText('${1:foo}-abc-$1', 'foo-abc-foo'); assertText('${1:foo}-abc-${1}', 'foo-abc-foo'); diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index 6c9ea6728db92..ed255770f69a4 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -434,6 +434,81 @@ suite('SnippetSession', function () { assertSelections(editor, new Selection(1, 6, 1, 25)); }); + test('snippets, transform', function () { + editor.getModel().setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, '${1/foo/bar/}$0'); + session.insert(); + assertSelections(editor, new Selection(1, 1, 1, 1)); + + editor.trigger('test', 'type', { text: 'foo' }); + session.next(); + + assert.equal(model.getValue(), 'bar'); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 4, 1, 4)); + }); + + test('snippets, multi placeholder same index one transform', function () { + editor.getModel().setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, '$1 baz ${1/foo/bar/}$0'); + session.insert(); + assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(1, 6, 1, 6)); + + editor.trigger('test', 'type', { text: 'foo' }); + session.next(); + + assert.equal(model.getValue(), 'foo baz bar'); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(1, 12, 1, 12)); + }); + + test('snippets, transform example', function () { + editor.getModel().setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, '${1:name} : ${2:type}${3: :=/\\s:=(.*)/${1:+ :=}${1}/};\n$0'); + session.insert(); + + assertSelections(editor, new Selection(1, 1, 1, 5)); + editor.trigger('test', 'type', { text: 'clk' }); + session.next(); + + assertSelections(editor, new Selection(1, 7, 1, 11)); + editor.trigger('test', 'type', { text: 'std_logic' }); + session.next(); + + assertSelections(editor, new Selection(1, 16, 1, 19)); + session.next(); + + assert.equal(model.getValue(), 'clk : std_logic;\n'); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(2, 1, 2, 1)); + }); + + test('snippets, transform example hit if', function () { + editor.getModel().setValue(''); + editor.setSelection(new Selection(1, 1, 1, 1)); + const session = new SnippetSession(editor, '${1:name} : ${2:type}${3: :=/\\s:=(.*)/${1:+ :=}${1}/};\n$0'); + session.insert(); + + assertSelections(editor, new Selection(1, 1, 1, 5)); + editor.trigger('test', 'type', { text: 'clk' }); + session.next(); + + assertSelections(editor, new Selection(1, 7, 1, 11)); + editor.trigger('test', 'type', { text: 'std_logic' }); + session.next(); + + assertSelections(editor, new Selection(1, 16, 1, 19)); + editor.trigger('test', 'type', { text: ' := \'1\'' }); + session.next(); + + assert.equal(model.getValue(), 'clk : std_logic := \'1\';\n'); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(2, 1, 2, 1)); + }); + test('Snippet placeholder index incorrect after using 2+ snippets in a row that each end with a placeholder, #30769', function () { editor.getModel().setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); From cd5afa88ef3cec155564df14afd6aa333906c2ba Mon Sep 17 00:00:00 2001 From: Christoph Seitz Date: Fri, 15 Jun 2018 12:53:26 +0200 Subject: [PATCH 008/149] Remove unused code. --- src/vs/editor/contrib/snippet/snippetParser.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index 93a992b3d17a0..7ab0d57eafeeb 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -500,9 +500,6 @@ export class TextmateSnippet extends Marker { let ret = 0; walk([marker], marker => { ret += marker.len(); - if (marker instanceof Transform) { - return false; - } return true; }); return ret; From a86d0e598d3e4b216c91a7278322a81df304982e Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 15 Jun 2018 17:49:18 +0200 Subject: [PATCH 009/149] add centeredViewLayout --- .../browser/ui/centered/centeredViewLayout.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/vs/base/browser/ui/centered/centeredViewLayout.ts diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts new file mode 100644 index 0000000000000..26842799d7696 --- /dev/null +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SplitView, Sizing, IView, Orientation } from 'vs/base/browser/ui/splitview/splitview'; +import { $ } from 'vs/base/browser/dom'; +import { Event } from 'vs/base/common/event'; + +export class CenteredViewLayout { + + private splitView: SplitView; + readonly element: HTMLElement; + + constructor(container: HTMLElement, view: IView) { + this.element = $('.centered-view-layout'); + container.appendChild(this.element); + + this.splitView = new SplitView(this.element, { + inverseAltBehavior: true, + orientation: Orientation.HORIZONTAL + }); + const addEmptyView = () => { + this.splitView.addView({ + element: $('.'), + layout: () => undefined, + minimumSize: 20, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: Event.None + }, Sizing.Distribute); + }; + + addEmptyView(); + this.splitView.addView(view, Sizing.Distribute); + addEmptyView(); + } + + layout(size: number): void { + this.splitView.layout(size); + } + + dispose(): void { + this.splitView.dispose(); + } +} From 98e0b5b66f80fc1de94c412658ac30c23cbdb2b5 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 15 Jun 2018 17:49:39 +0200 Subject: [PATCH 010/149] editorPart: plugin centeredViewLayout --- .../browser/parts/editor/editorPart.ts | 38 ++++++++++++++++- .../workbench/electron-browser/workbench.ts | 41 +++---------------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index bdac02ccfd833..e19bbe6bf3193 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -34,6 +34,7 @@ import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/ import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTarget'; import { localize } from 'vs/nls'; import { Color } from 'vs/base/common/color'; +import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout'; interface IEditorPartUIState { serializedGrid: ISerializedGrid; @@ -85,6 +86,8 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor private whenRestoredComplete: TValueCallback; private previousUIState: IEditorPartUIState; + private parentElement: HTMLElement; + private centeredViewLayout: CenteredViewLayout; constructor( id: string, @@ -695,11 +698,12 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor createContentArea(parent: HTMLElement): HTMLElement { // Grid control + this.parentElement = parent; this.doCreateGridControl(); // Container addClass(this.gridWidget.element, 'content'); - parent.appendChild(this.gridWidget.element); + this.parentElement.appendChild(this.gridWidget.element); // Drop support @@ -708,6 +712,29 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor return this.gridWidget.element; } + centerLayout(active: boolean): void { + if (!active && this.centeredViewLayout) { + this.parentElement.removeChild(this.centeredViewLayout.element); + this.parentElement.appendChild(this.gridWidget.element); + this.centeredViewLayout.dispose(); + this.centeredViewLayout = undefined; + } + + if (active) { + this.centeredViewLayout = new CenteredViewLayout(this.parentElement, { + element: this.gridWidget.element, + layout: size => this.gridWidget.layout(size, this.dimension ? this.dimension.height : this.gridWidget.maximumHeight), + minimumSize: this.gridWidget.minimumWidth, + maximumSize: this.gridWidget.maximumWidth, + onDidChange: Event.None + }); + } + } + + isLayoutCentered(): boolean { + return !!this.centeredViewLayout; + } + private doCreateGridControl(): void { // Grid Widget (with previous UI state) @@ -947,7 +974,11 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Layout Grid try { - this.gridWidget.layout(this.dimension.width, this.dimension.height); + if (this.centeredViewLayout) { + this.centeredViewLayout.layout(this.dimension.width); + } else { + this.gridWidget.layout(this.dimension.width, this.dimension.height); + } } catch (error) { this.gridError(error); } @@ -989,6 +1020,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor if (this.gridWidget) { this.gridWidget = dispose(this.gridWidget); } + if (this.centeredViewLayout) { + this.centeredViewLayout = dispose(this.centeredViewLayout); + } super.dispose(); } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index eb99329ae51c4..28bdfcecd9888 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -108,7 +108,7 @@ import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { PreferencesService } from 'vs/workbench/services/preferences/browser/preferencesService'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorGroupsService, GroupDirection, preferredSideBySideGroupDirection, GroupOrientation } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IEditorGroupsService, GroupDirection, preferredSideBySideGroupDirection } from 'vs/workbench/services/group/common/editorGroupsService'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IExtensionUrlHandler, ExtensionUrlHandler } from 'vs/platform/url/electron-browser/inactiveExtensionUrlHandler'; import { WorkbenchThemeService } from 'vs/workbench/services/themes/electron-browser/workbenchThemeService'; @@ -218,7 +218,6 @@ export class Workbench extends Disposable implements IPartService { private panelPosition: Position; private panelHidden: boolean; private zenMode: IZenMode; - private centeredEditorLayoutActive: boolean; private fontAliasing: FontAliasingOption; private hasInitialFilesToOpen: boolean; @@ -692,7 +691,7 @@ export class Workbench extends Disposable implements IPartService { // Restore Forced Editor Center Mode if (this.storageService.getBoolean(Workbench.centeredEditorLayoutActiveStorageKey, StorageScope.WORKSPACE, false)) { - this.centeredEditorLayoutActive = true; + this.centerEditorLayout(true); } const onRestored = (error?: Error): IWorkbenchStartedInfo => { @@ -846,9 +845,6 @@ export class Workbench extends Disposable implements IPartService { wasPanelVisible: false, transitionDisposeables: [] }; - - // Centered Editor Layout - this.centeredEditorLayoutActive = false; } private setPanelPositionFromStorageOrConfig() { @@ -1247,39 +1243,14 @@ export class Workbench extends Disposable implements IPartService { } isEditorLayoutCentered(): boolean { - return this.centeredEditorLayoutActive; + return this.editorPart.isLayoutCentered(); } - // TODO@ben support centered editor layout using empty groups or not? functionality missing: - // - resize sashes left and right in sync - // - IEditorInput.supportsCenteredEditorLayout() no longer supported - // - should we just allow to enter layout even if groups > 1? what does it then mean to be - // actively in centered editor layout though? centerEditorLayout(active: boolean, skipLayout?: boolean): void { - this.centeredEditorLayoutActive = active; - this.storageService.store(Workbench.centeredEditorLayoutActiveStorageKey, this.centeredEditorLayoutActive, StorageScope.WORKSPACE); + this.storageService.store(Workbench.centeredEditorLayoutActiveStorageKey, active, StorageScope.WORKSPACE); // Enter Centered Editor Layout - if (active) { - if (this.editorGroupService.count === 1) { - const activeGroup = this.editorGroupService.activeGroup; - this.editorGroupService.addGroup(activeGroup, GroupDirection.LEFT); - this.editorGroupService.addGroup(activeGroup, GroupDirection.RIGHT); - - this.editorGroupService.applyLayout({ groups: [{ size: 0.2 }, { size: 0.6 }, { size: 0.2 }], orientation: GroupOrientation.HORIZONTAL }); - } - } - - // Leave Centered Editor Layout - else { - if (this.editorGroupService.count === 3) { - this.editorGroupService.groups.forEach(group => { - if (group.count === 0) { - this.editorGroupService.removeGroup(group); - } - }); - } - } + this.editorPart.centerLayout(active); if (!skipLayout) { this.layout(); @@ -1460,4 +1431,4 @@ export class Workbench extends Disposable implements IPartService { } //#endregion -} \ No newline at end of file +} From e362c61effc3ea3d01aa664b65d4ced0e595cfab Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 15 Jun 2018 18:02:01 +0200 Subject: [PATCH 011/149] update the content class name depending on who is direct chald of editor part --- src/vs/workbench/browser/parts/editor/editorPart.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index e19bbe6bf3193..951dedd8061e0 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -720,6 +720,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this.centeredViewLayout = undefined; } + toggleClass(this.gridWidget.element, 'content', !active); if (active) { this.centeredViewLayout = new CenteredViewLayout(this.parentElement, { element: this.gridWidget.element, @@ -728,6 +729,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor maximumSize: this.gridWidget.maximumWidth, onDidChange: Event.None }); + addClass(this.centeredViewLayout.element, 'content'); } } From 151790c37d1fb3b51c2c0f1bac5977aae15af4e7 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 18 Jun 2018 11:56:16 +0200 Subject: [PATCH 012/149] center layout by adding empty views, do not reparent html elements --- .../browser/ui/centered/centeredViewLayout.ts | 35 +++++++++++++------ .../browser/parts/editor/editorPart.ts | 32 ++++++----------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 26842799d7696..6ed81c034a1f4 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -11,6 +11,7 @@ export class CenteredViewLayout { private splitView: SplitView; readonly element: HTMLElement; + private active: boolean; constructor(container: HTMLElement, view: IView) { this.element = $('.centered-view-layout'); @@ -20,25 +21,37 @@ export class CenteredViewLayout { inverseAltBehavior: true, orientation: Orientation.HORIZONTAL }); - const addEmptyView = () => { - this.splitView.addView({ - element: $('.'), - layout: () => undefined, - minimumSize: 20, - maximumSize: Number.POSITIVE_INFINITY, - onDidChange: Event.None - }, Sizing.Distribute); - }; - addEmptyView(); this.splitView.addView(view, Sizing.Distribute); - addEmptyView(); } layout(size: number): void { this.splitView.layout(size); } + isActive(): boolean { + return this.active; + } + + activate(active: boolean): void { + this.active = active; + if (this.active) { + const emptyView = { + element: $('.'), + layout: () => undefined, + minimumSize: 20, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: Event.None + }; + + this.splitView.addView(emptyView, Sizing.Distribute, 0); + this.splitView.addView(emptyView, Sizing.Distribute); + } else { + this.splitView.removeView(0); + this.splitView.removeView(1); + } + } + dispose(): void { this.splitView.dispose(); } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 69a122f1422f4..324daa3736785 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -711,9 +711,16 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor addClass(this.container, 'content'); parent.appendChild(this.container); - // Grid control + // Grid control with center layout this.doCreateGridControl(); - this.container.appendChild(this.gridWidget.element); + this.centeredViewLayout = new CenteredViewLayout(this.container, { + element: this.gridWidget.element, + layout: size => this.gridWidget.layout(size, this.dimension ? this.dimension.height : this.gridWidget.maximumHeight), + minimumSize: this.gridWidget.minimumWidth, + maximumSize: this.gridWidget.maximumWidth, + onDidChange: Event.None + }); + addClass(this.centeredViewLayout.element, 'content'); // Drop support this._register(this.instantiationService.createInstance(EditorDropTarget, this, this.container)); @@ -722,28 +729,11 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor } centerLayout(active: boolean): void { - if (!active && this.centeredViewLayout) { - this.container.removeChild(this.centeredViewLayout.element); - this.container.appendChild(this.gridWidget.element); - this.centeredViewLayout.dispose(); - this.centeredViewLayout = undefined; - } - - toggleClass(this.gridWidget.element, 'content', !active); - if (active) { - this.centeredViewLayout = new CenteredViewLayout(this.container, { - element: this.gridWidget.element, - layout: size => this.gridWidget.layout(size, this.dimension ? this.dimension.height : this.gridWidget.maximumHeight), - minimumSize: this.gridWidget.minimumWidth, - maximumSize: this.gridWidget.maximumWidth, - onDidChange: Event.None - }); - addClass(this.centeredViewLayout.element, 'content'); - } + this.centeredViewLayout.activate(active); } isLayoutCentered(): boolean { - return !!this.centeredViewLayout; + return this.centeredViewLayout.isActive(); } private doCreateGridControl(): void { From 01bafa17275aaab1f58d3976c7a88e3e36612276 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 18 Jun 2018 12:15:17 +0200 Subject: [PATCH 013/149] centered layout: respect separator style --- src/vs/base/browser/ui/centered/centeredViewLayout.ts | 6 +++++- src/vs/workbench/browser/parts/editor/editorPart.ts | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 6ed81c034a1f4..8e520d637108a 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SplitView, Sizing, IView, Orientation } from 'vs/base/browser/ui/splitview/splitview'; +import { SplitView, Sizing, IView, Orientation, ISplitViewStyles } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; @@ -33,6 +33,10 @@ export class CenteredViewLayout { return this.active; } + styles(style: ISplitViewStyles): void { + this.splitView.style(style); + } + activate(active: boolean): void { this.active = active; if (this.active) { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 324daa3736785..111668da7eae5 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -701,7 +701,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor protected updateStyles(): void { this.container.style.backgroundColor = this.getColor(editorBackground); - this.gridWidget.style({ separatorBorder: this.gridSeparatorBorder }); + const separatorBorderStyle = { separatorBorder: this.gridSeparatorBorder }; + this.gridWidget.style(separatorBorderStyle); + this.centeredViewLayout.styles(separatorBorderStyle); } createContentArea(parent: HTMLElement): HTMLElement { From 899aef9c9d50bb170bff15590b8ab8bb0d4cb5d4 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 18 Jun 2018 15:25:44 +0200 Subject: [PATCH 014/149] centerd view: address feedback --- .../browser/ui/centered/centeredViewLayout.ts | 87 +++++++++++++------ .../browser/parts/editor/editorPart.ts | 36 ++++---- 2 files changed, 79 insertions(+), 44 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 8e520d637108a..ea3b31ae99df7 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -3,60 +3,93 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SplitView, Sizing, IView, Orientation, ISplitViewStyles } from 'vs/base/browser/ui/splitview/splitview'; +import { SplitView, Sizing, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; +import { IView } from 'vs/base/browser/ui/grid/gridview'; export class CenteredViewLayout { private splitView: SplitView; - readonly element: HTMLElement; - private active: boolean; + private element: HTMLElement; + private height: number; + private style: ISplitViewStyles; - constructor(container: HTMLElement, view: IView) { - this.element = $('.centered-view-layout'); - container.appendChild(this.element); - - this.splitView = new SplitView(this.element, { - inverseAltBehavior: true, - orientation: Orientation.HORIZONTAL - }); - - this.splitView.addView(view, Sizing.Distribute); + constructor(private container: HTMLElement, private view: IView) { + this.container.appendChild(this.view.element); } - layout(size: number): void { - this.splitView.layout(size); + layout(width: number, height: number): void { + this.height = height; + if (this.splitView) { + this.splitView.layout(width); + } else { + this.view.layout(width, height); + } } isActive(): boolean { - return this.active; + return !!this.splitView; } styles(style: ISplitViewStyles): void { - this.splitView.style(style); + this.style = style; + if (this.splitView) { + this.splitView.style(this.style); + } + } + + resetView(view: IView): void { + this.view = view; + if (this.splitView) { + this.splitView.removeView(1); + this.splitView.addView(this.getView(), Sizing.Distribute, 1); + } + } + + private getView(): ISplitViewView { + return { + element: this.view.element, + maximumSize: this.view.maximumWidth, + minimumSize: this.view.minimumWidth, + onDidChange: Event.None, + layout: size => this.view.layout(size, this.height) + }; } activate(active: boolean): void { - this.active = active; - if (this.active) { - const emptyView = { - element: $('.'), + if (active) { + this.element = $('.centered-view-layout'); + this.container.removeChild(this.view.element); + this.container.appendChild(this.element); + this.splitView = new SplitView(this.element, { + inverseAltBehavior: true, + orientation: Orientation.HORIZONTAL, + styles: this.style + }); + + const getEmptyView = () => ({ + element: $('.centered-layout-margin'), layout: () => undefined, minimumSize: 20, maximumSize: Number.POSITIVE_INFINITY, onDidChange: Event.None - }; + }); - this.splitView.addView(emptyView, Sizing.Distribute, 0); - this.splitView.addView(emptyView, Sizing.Distribute); + this.splitView.addView(getEmptyView(), Sizing.Distribute); + this.splitView.addView(this.getView(), Sizing.Distribute); + this.splitView.addView(getEmptyView(), Sizing.Distribute); } else { - this.splitView.removeView(0); - this.splitView.removeView(1); + this.splitView.dispose(); + this.splitView = undefined; + this.container.removeChild(this.element); + this.container.appendChild(this.view.element); } } dispose(): void { - this.splitView.dispose(); + if (this.splitView) { + this.splitView.dispose(); + } } } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 111668da7eae5..e3e0891aa1f23 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -35,6 +35,7 @@ import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTa import { localize } from 'vs/nls'; import { Color } from 'vs/base/common/color'; import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout'; +import { IView } from 'vs/base/browser/ui/grid/gridview'; interface IEditorPartUIState { serializedGrid: ISerializedGrid; @@ -715,14 +716,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Grid control with center layout this.doCreateGridControl(); - this.centeredViewLayout = new CenteredViewLayout(this.container, { - element: this.gridWidget.element, - layout: size => this.gridWidget.layout(size, this.dimension ? this.dimension.height : this.gridWidget.maximumHeight), - minimumSize: this.gridWidget.minimumWidth, - maximumSize: this.gridWidget.maximumWidth, - onDidChange: Event.None - }); - addClass(this.centeredViewLayout.element, 'content'); + this.centeredViewLayout = new CenteredViewLayout(this.container, this.getGridAsView()); // Drop support this._register(this.instantiationService.createInstance(EditorDropTarget, this, this.container)); @@ -730,6 +724,18 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor return this.container; } + private getGridAsView(): IView { + return { + element: this.gridWidget.element, + layout: (width, height) => this.gridWidget.layout(width, height), + minimumWidth: this.gridWidget.minimumWidth, + maximumWidth: this.gridWidget.maximumWidth, + minimumHeight: this.gridWidget.minimumHeight, + maximumHeight: this.gridWidget.minimumHeight, + onDidChange: Event.None + }; + } + centerLayout(active: boolean): void { this.centeredViewLayout.activate(active); } @@ -829,7 +835,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this.gridWidget = gridWidget; if (gridWidget) { - this.container.appendChild(gridWidget.element); + if (this.centeredViewLayout) { + this.centeredViewLayout.resetView(this.getGridAsView()); + } this._onDidSizeConstraintsChange.input = gridWidget.onDidChange; } @@ -987,11 +995,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Layout Grid try { - if (this.centeredViewLayout) { - this.centeredViewLayout.layout(this.dimension.width); - } else { - this.gridWidget.layout(this.dimension.width, this.dimension.height); - } + this.centeredViewLayout.layout(this.dimension.width, this.dimension.height); } catch (error) { this.gridError(error); } @@ -1033,9 +1037,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor if (this.gridWidget) { this.gridWidget.dispose(); } - if (this.centeredViewLayout) { - this.centeredViewLayout = dispose(this.centeredViewLayout); - } + this.centeredViewLayout.dispose(); super.dispose(); } From 2370f863fa87e96ee211083b3d7f3f52bcaae0ab Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 18 Jun 2018 15:43:27 +0200 Subject: [PATCH 015/149] properly pass onDidChange and react to other feedback --- src/vs/base/browser/ui/centered/centeredViewLayout.ts | 9 +++++++-- src/vs/workbench/browser/parts/editor/editorPart.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index ea3b31ae99df7..418a6973aad9f 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -5,7 +5,7 @@ import { SplitView, Sizing, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; -import { Event } from 'vs/base/common/event'; +import { Event, mapEvent } from 'vs/base/common/event'; import { IView } from 'vs/base/browser/ui/grid/gridview'; export class CenteredViewLayout { @@ -44,6 +44,7 @@ export class CenteredViewLayout { if (this.splitView) { this.splitView.removeView(1); this.splitView.addView(this.getView(), Sizing.Distribute, 1); + this.splitView.distributeViewSizes(); } } @@ -52,12 +53,16 @@ export class CenteredViewLayout { element: this.view.element, maximumSize: this.view.maximumWidth, minimumSize: this.view.minimumWidth, - onDidChange: Event.None, + onDidChange: mapEvent(this.view.onDidChange, ({ width }) => width), layout: size => this.view.layout(size, this.height) }; } activate(active: boolean): void { + if (active === !!this.splitView) { + return; + } + if (active) { this.element = $('.centered-view-layout'); this.container.removeChild(this.view.element); diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index e3e0891aa1f23..f43f1221db3df 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -732,7 +732,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor maximumWidth: this.gridWidget.maximumWidth, minimumHeight: this.gridWidget.minimumHeight, maximumHeight: this.gridWidget.minimumHeight, - onDidChange: Event.None + onDidChange: this.gridWidget.onDidChange }; } From 8f7a6420fd8a21f327878f8d6a3b3dbdeb499773 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 18 Jun 2018 16:10:25 +0200 Subject: [PATCH 016/149] use helper functino to clarify toSplitViewView cast --- .../browser/ui/centered/centeredViewLayout.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 418a6973aad9f..664cb9d3e6d66 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -8,6 +8,16 @@ import { $ } from 'vs/base/browser/dom'; import { Event, mapEvent } from 'vs/base/common/event'; import { IView } from 'vs/base/browser/ui/grid/gridview'; +function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { + return { + element: view.element, + maximumSize: view.maximumWidth, + minimumSize: view.minimumWidth, + onDidChange: mapEvent(view.onDidChange, widthAndHeight => widthAndHeight ? widthAndHeight.width : 0), + layout: size => view.layout(size, getHeight()) + }; +} + export class CenteredViewLayout { private splitView: SplitView; @@ -43,21 +53,11 @@ export class CenteredViewLayout { this.view = view; if (this.splitView) { this.splitView.removeView(1); - this.splitView.addView(this.getView(), Sizing.Distribute, 1); + this.splitView.addView(toSplitViewView(this.view, () => this.height), Sizing.Distribute, 1); this.splitView.distributeViewSizes(); } } - private getView(): ISplitViewView { - return { - element: this.view.element, - maximumSize: this.view.maximumWidth, - minimumSize: this.view.minimumWidth, - onDidChange: mapEvent(this.view.onDidChange, ({ width }) => width), - layout: size => this.view.layout(size, this.height) - }; - } - activate(active: boolean): void { if (active === !!this.splitView) { return; @@ -82,7 +82,7 @@ export class CenteredViewLayout { }); this.splitView.addView(getEmptyView(), Sizing.Distribute); - this.splitView.addView(this.getView(), Sizing.Distribute); + this.splitView.addView(toSplitViewView(this.view, () => this.height), Sizing.Distribute); this.splitView.addView(getEmptyView(), Sizing.Distribute); } else { this.splitView.dispose(); From 4d2c0dd0b9f7a1e33a001a10f1d7842af9bc2981 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 18 Jun 2018 16:45:43 +0200 Subject: [PATCH 017/149] centeredViewLayout: height 100% to show watermark --- src/vs/base/browser/ui/centered/centeredViewLayout.css | 8 ++++++++ src/vs/base/browser/ui/centered/centeredViewLayout.ts | 2 ++ 2 files changed, 10 insertions(+) create mode 100644 src/vs/base/browser/ui/centered/centeredViewLayout.css diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.css b/src/vs/base/browser/ui/centered/centeredViewLayout.css new file mode 100644 index 0000000000000..854530732a0bb --- /dev/null +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench > .part.editor > .content .centered-view-layout { + height: 100%; +} diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 664cb9d3e6d66..1e3a40e319b63 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./centeredViewLayout'; + import { SplitView, Sizing, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; import { Event, mapEvent } from 'vs/base/common/event'; From 0aa7928d5eab92adedcd80eefe93d2edeae115b4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 19 Jun 2018 12:09:24 +1000 Subject: [PATCH 018/149] Re-enable rpm builds --- build/tfs/product-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/tfs/product-build.yml b/build/tfs/product-build.yml index 3e1a0246c74ed..1b713d88ddc31 100644 --- a/build/tfs/product-build.yml +++ b/build/tfs/product-build.yml @@ -278,7 +278,7 @@ phases: - script: | set -e npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb" - # npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" + npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" #npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-snap" AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ From 522eef39910a5a0b02c5be8a038fb10a75b584a3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 19 Jun 2018 12:09:43 +1000 Subject: [PATCH 019/149] Re-enable rpm publish --- build/tfs/linux/release.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/tfs/linux/release.sh b/build/tfs/linux/release.sh index 8843da6773426..ad74ce0a38561 100755 --- a/build/tfs/linux/release.sh +++ b/build/tfs/linux/release.sh @@ -31,10 +31,10 @@ DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_DEB package $DEB_FILENAME $VERSION true $DEB_PATH -# RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" -# RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" +RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" +RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" -# node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_RPM package $RPM_FILENAME $VERSION true $RPM_PATH +node build/tfs/common/publish.js $VSCODE_QUALITY $PLATFORM_RPM package $RPM_FILENAME $VERSION true $RPM_PATH # SNAP_FILENAME="$(ls $REPO/.build/linux/snap/$ARCH/ | grep .snap)" # SNAP_PATH="$REPO/.build/linux/snap/$ARCH/$SNAP_FILENAME" From f67fee1816ecf3e73a21c6372fa617963e77da66 Mon Sep 17 00:00:00 2001 From: bitshiftza <33934381+bitshiftza@users.noreply.github.com> Date: Tue, 19 Jun 2018 08:11:35 +0200 Subject: [PATCH 020/149] Fix #39458 --- .../contrib/goToDefinition/goToDefinitionMouse.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 13659526cab8d..774d502bdc98c 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -149,6 +149,16 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC for (; endLineNumber < maxLineNumber; endLineNumber++) { let endIndent = textEditorModel.getLineFirstNonWhitespaceColumn(endLineNumber); minIndent = Math.min(minIndent, endIndent); + + // Fix for https://github.com/Microsoft/vscode/issues/39458 + const rangeToTestForOpeningBracket = new Range(endLineNumber, 1, endLineNumber + 1, 1); + const contentWithPotentialOpeningBracket = textEditorModel.getValueInRange(rangeToTestForOpeningBracket) + .replace(new RegExp(`^\\s{${minIndent - 1}}`, 'gm'), '').trim(); + + if (contentWithPotentialOpeningBracket.length > 0 && contentWithPotentialOpeningBracket[0] === '{') { + continue; + } + if (startIndent === endIndent) { break; } From 11959fabe0926c6f3f8cdaca25167a6068c6fbcc Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 19 Jun 2018 16:19:24 +1000 Subject: [PATCH 021/149] Re-enable 32-bit builds too --- build/tfs/product-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/tfs/product-build.yml b/build/tfs/product-build.yml index 1b713d88ddc31..4e8f85af18f18 100644 --- a/build/tfs/product-build.yml +++ b/build/tfs/product-build.yml @@ -334,7 +334,7 @@ phases: - script: | set -e npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb" - # npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" + npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" #npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-snap" AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ From 74a101506f4b976ca05975630a10a0afbfb5966f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 09:17:25 +0200 Subject: [PATCH 022/149] Revert "adding titlebar icon svg" This reverts commit 8cadcf172ef58c42b270099d1973d633ce494b8d. --- package.json | 2 +- .../browser/parts/titlebar/media/code-icon.svg | 1 - .../parts/titlebar/media/titlebarpart.css | 8 +++----- .../browser/parts/titlebar/titlebarPart.ts | 17 +++++++++-------- 4 files changed, 13 insertions(+), 15 deletions(-) delete mode 100644 src/vs/workbench/browser/parts/titlebar/media/code-icon.svg diff --git a/package.json b/package.json index add4abb298022..35c5537d05bf6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.25.0", - "distro": "e781cd4cf9c4c77fa3c0fcb04017a3ba57cde7a2", + "distro": "5f30bb6859641677a2676f623f994f923e609469", "author": { "name": "Microsoft Corporation" }, diff --git a/src/vs/workbench/browser/parts/titlebar/media/code-icon.svg b/src/vs/workbench/browser/parts/titlebar/media/code-icon.svg deleted file mode 100644 index cc61f81ea5a24..0000000000000 --- a/src/vs/workbench/browser/parts/titlebar/media/code-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 770803a9a3443..fc61158b914e9 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -54,16 +54,14 @@ } .monaco-workbench > .part.titlebar > .window-appicon { - width: 35px; + width: 15px; + padding-left: 10px; margin-right: 113px; -webkit-app-region: no-drag; position: relative; z-index: 99; order: 1; - background-image: url('code-icon.svg'); - background-repeat: no-repeat; - background-position: center center; - background-size: 16px; + image-rendering: crisp-edges; } .monaco-workbench > .part.titlebar > .window-controls-container { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index a7dd3d25003d6..229ef8bfe919c 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -57,8 +57,8 @@ export class TitlebarPart extends Part implements ITitleService { titleFontSize?: number; titlebarHeight?: number; controlsWidth?: number; - appIconSize?: number; appIconWidth?: number; + appIconLeftPadding?: number; } = Object.create(null); private isInactive: boolean; @@ -251,8 +251,9 @@ export class TitlebarPart extends Part implements ITitleService { // App Icon (Windows/Linux) if (!isMacintosh) { - this.appIcon = $(this.titleContainer).div({ + this.appIcon = $(this.titleContainer).img({ class: 'window-appicon', + src: paths.join(this.environmentService.appRoot, isWindows ? 'resources/win32/code.ico' : 'resources/linux/code.png') }).on(EventType.DBLCLICK, e => { EventHelper.stop(e, true); @@ -456,14 +457,14 @@ export class TitlebarPart extends Part implements ITitleService { this.initialSizing.appIconWidth = parseInt(this.appIcon.getComputedStyle().width, 10); } - if (typeof this.initialSizing.appIconSize !== 'number') { - this.initialSizing.appIconSize = parseInt(this.appIcon.getComputedStyle().backgroundSize, 10); + if (typeof this.initialSizing.appIconLeftPadding !== 'number') { + this.initialSizing.appIconLeftPadding = parseInt(this.appIcon.getComputedStyle().paddingLeft, 10); } const currentAppIconHeight = parseInt(this.appIcon.getComputedStyle().height, 10); const newControlsWidth = this.initialSizing.controlsWidth / getZoomFactor(); const newAppIconWidth = this.initialSizing.appIconWidth / getZoomFactor(); - const newAppIconSize = this.initialSizing.appIconSize / getZoomFactor(); + const newAppIconPaddingLeft = this.initialSizing.appIconLeftPadding / getZoomFactor(); if (!this.menubarWidth) { this.menubarWidth = 0; @@ -482,9 +483,9 @@ export class TitlebarPart extends Part implements ITitleService { // Adjust app icon mimic menubar this.appIcon.style({ - 'width': `${newAppIconWidth}px`, - 'background-size': `${newAppIconSize}px`, - 'margin-right': `${newControlsWidth - newAppIconWidth + bufferWidth}px`, + width: `${newAppIconWidth}px`, + 'padding-left': `${newAppIconPaddingLeft}px`, + 'margin-right': `${newControlsWidth - newAppIconWidth - newAppIconPaddingLeft + bufferWidth}px`, 'padding-top': `${(newHeight - currentAppIconHeight) / 2.0}px`, 'padding-bottom': `${(newHeight - currentAppIconHeight) / 2.0}px` }); From 30bd33d421b689b63565282764ef455ddf9ad659 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 09:24:26 +0200 Subject: [PATCH 023/149] fix #52273 --- src/vs/workbench/electron-browser/workbench.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 397fde34ad5e3..7068b59ac50cd 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -1188,7 +1188,7 @@ export class Workbench extends Disposable implements IPartService { case Parts.TITLEBAR_PART: return this.getCustomTitleBarStyle() === 'custom' && !browser.isFullscreen(); case Parts.MENUBAR_PART: - return this.getCustomTitleBarStyle() === 'custom' && !this.menubarHidden; + return this.isVisible(Parts.TITLEBAR_PART) && !this.menubarHidden; case Parts.SIDEBAR_PART: return !this.sideBarHidden; case Parts.PANEL_PART: From e28c26502525ce188b611529b69b7377d742aca1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 09:24:47 +0200 Subject: [PATCH 024/149] Revert "Revert "adding titlebar icon svg"" This reverts commit 74a101506f4b976ca05975630a10a0afbfb5966f. --- package.json | 2 +- .../browser/parts/titlebar/media/code-icon.svg | 1 + .../parts/titlebar/media/titlebarpart.css | 8 +++++--- .../browser/parts/titlebar/titlebarPart.ts | 17 ++++++++--------- 4 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 src/vs/workbench/browser/parts/titlebar/media/code-icon.svg diff --git a/package.json b/package.json index 35c5537d05bf6..add4abb298022 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.25.0", - "distro": "5f30bb6859641677a2676f623f994f923e609469", + "distro": "e781cd4cf9c4c77fa3c0fcb04017a3ba57cde7a2", "author": { "name": "Microsoft Corporation" }, diff --git a/src/vs/workbench/browser/parts/titlebar/media/code-icon.svg b/src/vs/workbench/browser/parts/titlebar/media/code-icon.svg new file mode 100644 index 0000000000000..cc61f81ea5a24 --- /dev/null +++ b/src/vs/workbench/browser/parts/titlebar/media/code-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index fc61158b914e9..770803a9a3443 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -54,14 +54,16 @@ } .monaco-workbench > .part.titlebar > .window-appicon { - width: 15px; - padding-left: 10px; + width: 35px; margin-right: 113px; -webkit-app-region: no-drag; position: relative; z-index: 99; order: 1; - image-rendering: crisp-edges; + background-image: url('code-icon.svg'); + background-repeat: no-repeat; + background-position: center center; + background-size: 16px; } .monaco-workbench > .part.titlebar > .window-controls-container { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 229ef8bfe919c..a7dd3d25003d6 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -57,8 +57,8 @@ export class TitlebarPart extends Part implements ITitleService { titleFontSize?: number; titlebarHeight?: number; controlsWidth?: number; + appIconSize?: number; appIconWidth?: number; - appIconLeftPadding?: number; } = Object.create(null); private isInactive: boolean; @@ -251,9 +251,8 @@ export class TitlebarPart extends Part implements ITitleService { // App Icon (Windows/Linux) if (!isMacintosh) { - this.appIcon = $(this.titleContainer).img({ + this.appIcon = $(this.titleContainer).div({ class: 'window-appicon', - src: paths.join(this.environmentService.appRoot, isWindows ? 'resources/win32/code.ico' : 'resources/linux/code.png') }).on(EventType.DBLCLICK, e => { EventHelper.stop(e, true); @@ -457,14 +456,14 @@ export class TitlebarPart extends Part implements ITitleService { this.initialSizing.appIconWidth = parseInt(this.appIcon.getComputedStyle().width, 10); } - if (typeof this.initialSizing.appIconLeftPadding !== 'number') { - this.initialSizing.appIconLeftPadding = parseInt(this.appIcon.getComputedStyle().paddingLeft, 10); + if (typeof this.initialSizing.appIconSize !== 'number') { + this.initialSizing.appIconSize = parseInt(this.appIcon.getComputedStyle().backgroundSize, 10); } const currentAppIconHeight = parseInt(this.appIcon.getComputedStyle().height, 10); const newControlsWidth = this.initialSizing.controlsWidth / getZoomFactor(); const newAppIconWidth = this.initialSizing.appIconWidth / getZoomFactor(); - const newAppIconPaddingLeft = this.initialSizing.appIconLeftPadding / getZoomFactor(); + const newAppIconSize = this.initialSizing.appIconSize / getZoomFactor(); if (!this.menubarWidth) { this.menubarWidth = 0; @@ -483,9 +482,9 @@ export class TitlebarPart extends Part implements ITitleService { // Adjust app icon mimic menubar this.appIcon.style({ - width: `${newAppIconWidth}px`, - 'padding-left': `${newAppIconPaddingLeft}px`, - 'margin-right': `${newControlsWidth - newAppIconWidth - newAppIconPaddingLeft + bufferWidth}px`, + 'width': `${newAppIconWidth}px`, + 'background-size': `${newAppIconSize}px`, + 'margin-right': `${newControlsWidth - newAppIconWidth + bufferWidth}px`, 'padding-top': `${(newHeight - currentAppIconHeight) / 2.0}px`, 'padding-bottom': `${(newHeight - currentAppIconHeight) / 2.0}px` }); From b4a18da6e78f95295ea6538f5ae8dd0c9f0869a9 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Jun 2018 09:14:46 +0200 Subject: [PATCH 025/149] fixes #52251 --- src/vs/base/browser/ui/splitview/splitview.ts | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 4272aa50abf90..2022b7df42433 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -346,13 +346,18 @@ export class SplitView implements IDisposable { const toSize = this.getViewSize(to); const toView = this.removeView(to); const fromView = this.removeView(from); + this.addView(toView, fromSize, from); this.addView(fromView, toSize, to); } private relayout(lowPriorityIndex?: number, highPriorityIndex?: number): void { const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); - this.resizeAndLayout(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex, highPriorityIndex); + + this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex, highPriorityIndex); + this.distributeEmptySpace(); + this.layoutViews(); + this.saveProportions(); } layout(size: number): void { @@ -360,14 +365,21 @@ export class SplitView implements IDisposable { this.size = size; if (!this.proportions) { - this.resizeAndLayout(this.viewItems.length - 1, size - previousSize); + this.resize(this.viewItems.length - 1, size - previousSize); } else { for (let i = 0; i < this.viewItems.length; i++) { const item = this.viewItems[i]; item.size = clamp(Math.round(this.proportions[i] * size), item.view.minimumSize, item.view.maximumSize); } + } + + this.distributeEmptySpace(); + this.layoutViews(); + } - this.layoutViews(); + private saveProportions(): void { + if (this.contentSize > 0) { + this.proportions = this.viewItems.map(i => i.size / this.contentSize); } } @@ -432,12 +444,14 @@ export class SplitView implements IDisposable { this.resize(resizeIndex, -newDelta, newSizes, undefined, undefined, newMinDelta, newMaxDelta); } + this.distributeEmptySpace(); this.layoutViews(); } private onSashEnd(index: number): void { this._onDidSashChange.fire(index); this.sashDragState.disposable.dispose(); + this.saveProportions(); } private onViewChange(item: IViewItem, size: number | undefined): void { @@ -475,7 +489,7 @@ export class SplitView implements IDisposable { const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0); const deltaDown = clamp(delta, -expandDown, collapseDown); - this.resizeAndLayout(index, deltaDown); + this.resize(index, deltaDown); delta -= deltaDown; } @@ -485,9 +499,12 @@ export class SplitView implements IDisposable { const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0); const deltaUp = clamp(-delta, -collapseUp, expandUp); - this.resizeAndLayout(index - 1, deltaUp); + this.resize(index - 1, deltaUp); } + this.distributeEmptySpace(); + this.layoutViews(); + this.saveProportions(); this.state = State.Idle; } @@ -571,23 +588,7 @@ export class SplitView implements IDisposable { return delta; } - private resizeAndLayout( - index: number, - delta: number, - sizes = this.viewItems.map(i => i.size), - lowPriorityIndex?: number, - highPriorityIndex?: number - ): void { - this.resize(index, delta, sizes, lowPriorityIndex, highPriorityIndex); - this.layoutViews(); - - if (this.contentSize > 0) { - this.proportions = this.viewItems.map(i => i.size / this.contentSize); - } - } - - private layoutViews(): void { - // Rebalance empty space + private distributeEmptySpace(): void { let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); let emptyDelta = this.size - contentSize; @@ -599,7 +600,9 @@ export class SplitView implements IDisposable { emptyDelta -= viewDelta; item.size = size; } + } + private layoutViews(): void { // Save new content size this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); From 7edc9aadca869b5191e974f39eab5f793e34569f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Jun 2018 09:15:56 +0200 Subject: [PATCH 026/149] :lipstick: --- src/vs/base/browser/ui/splitview/splitview.ts | 40 +---------------- src/vs/base/common/arrays.ts | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 2022b7df42433..17501bcfafbf8 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -11,7 +11,7 @@ import { Event, mapEvent, Emitter } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; import * as dom from 'vs/base/browser/dom'; import { clamp } from 'vs/base/common/numbers'; -import { range, firstIndex } from 'vs/base/common/arrays'; +import { range, firstIndex, pushToStart, pushToEnd } from 'vs/base/common/arrays'; import { Sash, Orientation, ISashEvent as IBaseSashEvent, SashState } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; import { domEvent } from 'vs/base/browser/event'; @@ -77,44 +77,6 @@ enum State { Busy } -function pushToStart(arr: T[], value: T): T[] { - let didFindValue = false; - - const result = arr.filter(v => { - if (v === value) { - didFindValue = true; - return false; - } - - return true; - }); - - if (didFindValue) { - result.unshift(value); - } - - return result; -} - -function pushToEnd(arr: T[], value: T): T[] { - let didFindValue = false; - - const result = arr.filter(v => { - if (v === value) { - didFindValue = true; - return false; - } - - return true; - }); - - if (didFindValue) { - result.push(value); - } - - return result; -} - export type DistributeSizing = { type: 'distribute' }; export type SplitSizing = { type: 'split', index: number }; export type Sizing = DistributeSizing | SplitSizing; diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index f9ddfdc71aaec..2de4ad21a44d7 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -463,3 +463,47 @@ export function shuffle(array: T[]): void { array[j] = temp; } } + +/** + * Pushes an element to the start of the array, if found. + */ +export function pushToStart(arr: T[], value: T): T[] { + let didFindValue = false; + + const result = arr.filter(v => { + if (v === value) { + didFindValue = true; + return false; + } + + return true; + }); + + if (didFindValue) { + result.unshift(value); + } + + return result; +} + +/** + * Pushes an element to the end of the array, if found. + */ +export function pushToEnd(arr: T[], value: T): T[] { + let didFindValue = false; + + const result = arr.filter(v => { + if (v === value) { + didFindValue = true; + return false; + } + + return true; + }); + + if (didFindValue) { + result.push(value); + } + + return result; +} \ No newline at end of file From be4551065916f61e265df85bd13d46b6779c95e1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Jun 2018 09:23:18 +0200 Subject: [PATCH 027/149] splitview: better naming --- src/vs/base/browser/ui/splitview/splitview.ts | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 17501bcfafbf8..f3b36da783289 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -356,10 +356,9 @@ export class SplitView implements IDisposable { const resetSashDragState = (start: number, alt: boolean) => { const sizes = this.viewItems.map(i => i.size); - - // TODO@Joao rename these guys - let minDelta = Number.POSITIVE_INFINITY; + let minDelta = Number.NEGATIVE_INFINITY; let maxDelta = Number.POSITIVE_INFINITY; + if (this.inverseAltBehavior) { alt = !alt; } @@ -372,11 +371,11 @@ export class SplitView implements IDisposable { if (isLastSash) { const viewItem = this.viewItems[index]; - minDelta = (viewItem.size - viewItem.view.minimumSize) / 2; + minDelta = (viewItem.view.minimumSize - viewItem.size) / 2; maxDelta = (viewItem.view.maximumSize - viewItem.size) / 2; } else { const viewItem = this.viewItems[index + 1]; - minDelta = (viewItem.view.maximumSize - viewItem.size) / 2; + minDelta = (viewItem.size - viewItem.view.maximumSize) / 2; maxDelta = (viewItem.size - viewItem.view.minimumSize) / 2; } } @@ -399,8 +398,8 @@ export class SplitView implements IDisposable { const newSizes = this.viewItems.map(i => i.size); const viewItemIndex = isLastSash ? index : index + 1; const viewItem = this.viewItems[viewItemIndex]; - const newMinDelta = (viewItem.view.maximumSize - viewItem.size); - const newMaxDelta = (viewItem.size - viewItem.view.minimumSize); + const newMinDelta = viewItem.size - viewItem.view.maximumSize; + const newMaxDelta = viewItem.size - viewItem.view.minimumSize; const resizeIndex = isLastSash ? index - 1 : index + 1; this.resize(resizeIndex, -newDelta, newSizes, undefined, undefined, newMinDelta, newMaxDelta); @@ -492,7 +491,7 @@ export class SplitView implements IDisposable { sizes = this.viewItems.map(i => i.size), lowPriorityIndex?: number, highPriorityIndex?: number, - overloadMinDelta: number = Number.POSITIVE_INFINITY, + overloadMinDelta: number = Number.NEGATIVE_INFINITY, overloadMaxDelta: number = Number.POSITIVE_INFINITY ): number { if (index < 0 || index >= this.viewItems.length) { @@ -518,14 +517,12 @@ export class SplitView implements IDisposable { const downItems = downIndexes.map(i => this.viewItems[i]); const downSizes = downIndexes.map(i => sizes[i]); - const collapseUp = upIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0); - const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0); - - const collapseDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0); - const expandDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0); - - const minDelta = -Math.min(collapseUp, expandDown, overloadMinDelta); - const maxDelta = Math.min(collapseDown, expandUp, overloadMaxDelta); + const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.minimumSize - sizes[i]), 0); + const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0); + const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0); + const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.maximumSize), 0); + const minDelta = Math.max(minDeltaUp, minDeltaDown, overloadMinDelta); + const maxDelta = Math.min(maxDeltaDown, maxDeltaUp, overloadMaxDelta); delta = clamp(delta, minDelta, maxDelta); From e60eaf71a839fd75ce8cbf8786dc6023cb63a3f7 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Jun 2018 09:30:57 +0200 Subject: [PATCH 028/149] splitview: more :lipstick: --- src/vs/base/browser/ui/splitview/splitview.ts | 12 +++--- src/vs/base/common/arrays.ts | 40 +++++-------------- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index f3b36da783289..9984105f512fe 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -498,17 +498,17 @@ export class SplitView implements IDisposable { return 0; } - let upIndexes = range(index, -1); - let downIndexes = range(index + 1, this.viewItems.length); + const upIndexes = range(index, -1); + const downIndexes = range(index + 1, this.viewItems.length); if (typeof highPriorityIndex === 'number') { - upIndexes = pushToStart(upIndexes, highPriorityIndex); - downIndexes = pushToStart(downIndexes, highPriorityIndex); + pushToStart(upIndexes, highPriorityIndex); + pushToStart(downIndexes, highPriorityIndex); } if (typeof lowPriorityIndex === 'number') { - upIndexes = pushToEnd(upIndexes, lowPriorityIndex); - downIndexes = pushToEnd(downIndexes, lowPriorityIndex); + pushToEnd(upIndexes, lowPriorityIndex); + pushToEnd(downIndexes, lowPriorityIndex); } const upItems = upIndexes.map(i => this.viewItems[i]); diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 2de4ad21a44d7..ef84fe95d9a28 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -467,43 +467,23 @@ export function shuffle(array: T[]): void { /** * Pushes an element to the start of the array, if found. */ -export function pushToStart(arr: T[], value: T): T[] { - let didFindValue = false; +export function pushToStart(arr: T[], value: T): void { + const index = arr.indexOf(value); - const result = arr.filter(v => { - if (v === value) { - didFindValue = true; - return false; - } - - return true; - }); - - if (didFindValue) { - result.unshift(value); + if (index > -1) { + arr.splice(index, 1); + arr.unshift(value); } - - return result; } /** * Pushes an element to the end of the array, if found. */ -export function pushToEnd(arr: T[], value: T): T[] { - let didFindValue = false; +export function pushToEnd(arr: T[], value: T): void { + const index = arr.indexOf(value); - const result = arr.filter(v => { - if (v === value) { - didFindValue = true; - return false; - } - - return true; - }); - - if (didFindValue) { - result.push(value); + if (index > -1) { + arr.splice(index, 1); + arr.push(value); } - - return result; } \ No newline at end of file From 4af239efe4e9a9fa1c7f18e2b902345595deae0a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 19 Jun 2018 09:31:07 +0200 Subject: [PATCH 029/149] splitview: split sizing tests --- .../browser/ui/splitview/splitview.test.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 3b70bf44ac1c2..619fa4dfa14b7 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -387,4 +387,48 @@ suite('Splitview', () => { view2.dispose(); view1.dispose(); }); + + test('split sizing', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + assert.equal(view1.size, 200); + + splitview.addView(view2, Sizing.Split(0)); + assert.deepEqual([view1.size, view2.size], [100, 100]); + + splitview.addView(view3, Sizing.Split(1)); + assert.deepEqual([view1.size, view2.size, view3.size], [100, 50, 50]); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('split sizing 2', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + assert.equal(view1.size, 200); + + splitview.addView(view2, Sizing.Split(0)); + assert.deepEqual([view1.size, view2.size], [100, 100]); + + splitview.addView(view3, Sizing.Split(0)); + assert.deepEqual([view1.size, view2.size, view3.size], [50, 100, 50]); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); }); \ No newline at end of file From 933ac4b90f2cc5751f1e4763d92cde60b2cf0f75 Mon Sep 17 00:00:00 2001 From: Darius Keeley Date: Tue, 19 Jun 2018 09:25:02 +0100 Subject: [PATCH 030/149] Fix #51533 (#52075) --- .../browser/parts/editor/tabsTitleControl.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 77946f8ec86fd..8934bb9925f44 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -1129,13 +1129,13 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const adjustedColor = tabHoverBackground.flatten(adjustedTabBackground); const adjustedColorDrag = tabHoverBackground.flatten(adjustedTabDragBackground); collector.addRule(` - .monaco-workbench > .part.editor > .content:not(.dragged-over) .editor-group-container > .title.active .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { - background: linear-gradient(to left, ${adjustedColor}, transparent); + .monaco-workbench > .part.editor > .content:not(.dragged-over) .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { + background: linear-gradient(to left, ${adjustedColor}, transparent) !important; } - .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container > .title.active .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { - background: linear-gradient(to left, ${adjustedColorDrag}, transparent); + .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { + background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important; } `); } @@ -1146,11 +1146,11 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const adjustedColorDrag = tabUnfocusedHoverBackground.flatten(adjustedTabDragBackground); collector.addRule(` .monaco-workbench > .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { - background: linear-gradient(to left, ${adjustedColor}, transparent); + background: linear-gradient(to left, ${adjustedColor}, transparent) !important; } .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after { - background: linear-gradient(to left, ${adjustedColorDrag}, transparent); + background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important; } `); } @@ -1161,7 +1161,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { collector.addRule(` .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.active):not(.dragged) > .tab-label::after, .monaco-workbench > .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink.dragged-over:not(.dragged) > .tab-label::after { - background: linear-gradient(to left, ${adjustedColorDrag}, transparent); + background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important; } `); } @@ -1188,7 +1188,6 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const adjustedColor = tabInactiveBackground.flatten(adjustedTabBackground); const adjustedColorDrag = tabInactiveBackground.flatten(adjustedTabDragBackground); collector.addRule(` - .monaco-workbench > .part.editor > .content .editor-group-container > .title .monaco-workbench > .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged) > .tab-label::after { background: linear-gradient(to left, ${adjustedColor}, transparent); } From d3d7165135bcc29238e1f5c97d7f8d1f3d27ca1f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 10:33:48 +0200 Subject: [PATCH 031/149] tabs - ensure tab border draws over title border --- .../browser/parts/editor/media/tabstitlecontrol.css | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 8ae86117aa7ed..2dc523ad8aab4 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -94,11 +94,10 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container { display: block; - content: ''; position: absolute; top: 0; left: 0; - z-index: 5; + z-index: 6; /* over possible title border */ pointer-events: none; background-color: var(--tab-border-top-color); width: 100%; @@ -107,11 +106,10 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container { display: block; - content: ''; position: absolute; bottom: 0; left: 0; - z-index: 5; + z-index: 6; /* over possible title border */ pointer-events: none; background-color: var(--tab-border-bottom-color); width: 100%; From b8b60dd48763e13fc95e72b40e74912dac1e2ec7 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Thu, 14 Jun 2018 16:59:30 +0200 Subject: [PATCH 032/149] Remove core language pack from build --- build/gulpfile.extensions.js | 8 ++------ build/gulpfile.vscode.js | 14 +------------- build/lib/optimize.js | 5 ----- build/lib/optimize.ts | 10 +--------- 4 files changed, 4 insertions(+), 33 deletions(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 3ec638c2f2292..acc7d90bfb7f2 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -20,7 +20,6 @@ const sourcemaps = require('gulp-sourcemaps'); const nlsDev = require('vscode-nls-dev'); const root = path.dirname(__dirname); const commit = util.getVersion(root); -const i18n = require('./lib/i18n'); const plumber = require('gulp-plumber'); const extensionsPath = path.join(path.dirname(__dirname), 'extensions'); @@ -32,8 +31,6 @@ const compilations = glob.sync('**/tsconfig.json', { const getBaseUrl = out => `https://ticino.blob.core.windows.net/sourcemaps/${commit}/${out}`; -const languages = i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); - const tasks = compilations.map(function (tsconfigFile) { const absolutePath = path.join(extensionsPath, tsconfigFile); const relativeDirname = path.dirname(tsconfigFile); @@ -58,7 +55,6 @@ const tasks = compilations.map(function (tsconfigFile) { const srcBase = path.join(root, 'src'); const src = path.join(srcBase, '**'); const out = path.join(root, 'out'); - const i18nPath = path.join(__dirname, '..', 'i18n'); const baseUrl = getBaseUrl(out); let headerId, headerOut; @@ -102,9 +98,9 @@ const tasks = compilations.map(function (tsconfigFile) { sourceRoot: '../src' })) .pipe(tsFilter.restore) - .pipe(build ? nlsDev.createAdditionalLanguageFiles(languages, i18nPath, out) : es.through()) .pipe(build ? nlsDev.bundleMetaDataFiles(headerId, headerOut) : es.through()) - .pipe(build ? nlsDev.bundleLanguageFiles() : es.through()) + // Filter out *.nls.json file. We needed them only to bundle meta data file. + .pipe(filter(['**', '!**/*.nls.json'])) .pipe(reporter.end(emitError)); return es.duplex(input, output); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 30a1563d7b9c1..47f243676b54d 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -17,14 +17,12 @@ const vfs = require('vinyl-fs'); const rename = require('gulp-rename'); const replace = require('gulp-replace'); const filter = require('gulp-filter'); -const buffer = require('gulp-buffer'); const json = require('gulp-json-editor'); const _ = require('underscore'); const util = require('./lib/util'); const ext = require('./lib/extensions'); const buildfile = require('../src/buildfile'); const common = require('./lib/optimize'); -const nlsDev = require('vscode-nls-dev'); const root = path.dirname(__dirname); const commit = util.getVersion(root); // @ts-ignore Microsoft/TypeScript#21262 complains about a require of a JSON file @@ -98,8 +96,6 @@ const BUNDLED_FILE_HEADER = [ ' *--------------------------------------------------------*/' ].join('\n'); -const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); - gulp.task('clean-optimized-vscode', util.rimraf('out-vscode')); gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({ entryPoints: vscodeEntryPoints, @@ -108,7 +104,6 @@ gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compil loaderConfig: common.loaderConfig(nodeModules), header: BUNDLED_FILE_HEADER, out: 'out-vscode', - languages: languages, bundleInfo: undefined })); @@ -244,15 +239,8 @@ function packageTask(platform, arch, opts) { .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); const localExtensions = es.merge(...localExtensionDescriptions.map(extension => { - const nlsFilter = filter('**/*.nls.json', { restore: true }); - return ext.fromLocal(extension.path) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)) - // // TODO@Dirk: this filter / buffer is here to make sure the nls.json files are buffered - .pipe(nlsFilter) - .pipe(buffer()) - .pipe(nlsDev.createAdditionalLanguageFiles(languages, path.join(__dirname, '..', 'i18n'))) - .pipe(nlsFilter.restore); + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); })); const localExtensionDependencies = gulp.src('extensions/node_modules/**', { base: '.' }); diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 2693d9f519aae..3599086a4b43b 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -17,7 +17,6 @@ var concat = require("gulp-concat"); var VinylFile = require("vinyl"); var bundle = require("./bundle"); var util = require("./util"); -var i18n = require("./i18n"); var gulpUtil = require("gulp-util"); var flatmap = require("gulp-flatmap"); var pump = require("pump"); @@ -163,10 +162,6 @@ function optimizeTask(opts) { sourceRoot: null, addComment: true, includeContent: true - })) - .pipe(i18n.processNlsFiles({ - fileHeader: bundledFileHeader, - languages: opts.languages })) .pipe(gulp.dest(out)); }; diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index b5636ffb8ef2d..7c5f15309c3c6 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -18,7 +18,6 @@ import * as concat from 'gulp-concat'; import * as VinylFile from 'vinyl'; import * as bundle from './bundle'; import * as util from './util'; -import * as i18n from './i18n'; import * as gulpUtil from 'gulp-util'; import * as flatmap from 'gulp-flatmap'; import * as pump from 'pump'; @@ -160,11 +159,8 @@ export interface IOptimizeTaskOpts { * (out folder name) */ out: string; - /** - * (languages to process) - */ - languages: i18n.Language[]; } + export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream { const entryPoints = opts.entryPoints; const otherSources = opts.otherSources; @@ -233,10 +229,6 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr addComment: true, includeContent: true })) - .pipe(i18n.processNlsFiles({ - fileHeader: bundledFileHeader, - languages: opts.languages - })) .pipe(gulp.dest(out)); }; } From 04cc5794d1e2244f98f5017542121ebd047d363f Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Fri, 15 Jun 2018 10:36:41 +0200 Subject: [PATCH 033/149] Fixes #51982: Platform.locale doesn't always point to the requested locale as speced. --- src/main.js | 45 ++++++---------------------------- src/vs/base/common/platform.ts | 2 ++ 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/main.js b/src/main.js index 268e143d53cf0..135637642741f 100644 --- a/src/main.js +++ b/src/main.js @@ -230,50 +230,21 @@ function getNLSConfiguration(locale) { let initialLocale = locale; - function resolveLocale(locale) { - while (locale) { - let candidate = path.join(__dirname, 'vs', 'code', 'electron-main', 'main.nls.') + locale + '.js'; - if (fs.existsSync(candidate)) { - return { locale: initialLocale, availableLanguages: { '*': locale } }; - } else { - let index = locale.lastIndexOf('-'); - if (index > 0) { - locale = locale.substring(0, index); - } else { - locale = undefined; - } - } - } - return undefined; - } - perf.mark('nlsGeneration:start'); - let defaultResult = function (locale) { - let isCoreLanguage = true; - if (locale) { - isCoreLanguage = ['de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-cn', 'zh-tw'].some((language) => { - return locale === language || locale.startsWith(language + '-'); - }); - } - if (isCoreLanguage) { - let result = resolveLocale(locale); - perf.mark('nlsGeneration:end'); - return Promise.resolve(result); - } else { - perf.mark('nlsGeneration:end'); - return Promise.resolve({ locale: locale, availableLanguages: {} }); - } + + let defaultResult = function(locale) { + perf.mark('nlsGeneration:end'); + return Promise.resolve({ locale: locale, availableLanguages: {} }); }; try { let commit = product.commit; if (!commit) { - return defaultResult(locale); + return defaultResult(initialLocale); } let configs = getLanguagePackConfigurations(); if (!configs) { - return defaultResult(locale); + return defaultResult(initialLocale); } - let initialLocale = locale; locale = resolveLanguagePackLocale(configs, locale); if (!locale) { return defaultResult(initialLocale); @@ -281,11 +252,11 @@ function getNLSConfiguration(locale) { let packConfig = configs[locale]; let mainPack; if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') { - return defaultResult(locale); + return defaultResult(initialLocale); } return exists(mainPack).then((fileExists) => { if (!fileExists) { - return defaultResult(locale); + return defaultResult(initialLocale); } let packId = packConfig.hash + '.' + locale; let cacheRoot = path.join(userData, 'clp', packId); diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 0bb890d29bfca..277929ed5d022 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -46,6 +46,8 @@ if (typeof process === 'object' && typeof process.nextTick === 'function' && typ _isWindows = (process.platform === 'win32'); _isMacintosh = (process.platform === 'darwin'); _isLinux = (process.platform === 'linux'); + _locale = LANGUAGE_DEFAULT; + _language = LANGUAGE_DEFAULT; const rawNlsConfig = process.env['VSCODE_NLS_CONFIG']; if (rawNlsConfig) { try { From 8527801364f831933850162ad2fa70b565514c36 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Jun 2018 10:51:04 +0200 Subject: [PATCH 034/149] centeredView: store ratios and restore them properly --- .../browser/ui/centered/centeredViewLayout.ts | 50 +++++++++++++++---- .../browser/parts/editor/editorPart.ts | 7 ++- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 1e3a40e319b63..c3c97743d8b38 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -5,39 +5,59 @@ import 'vs/css!./centeredViewLayout'; -import { SplitView, Sizing, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; +import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; -import { Event, mapEvent } from 'vs/base/common/event'; +import { Event, mapEvent, anyEvent } from 'vs/base/common/event'; import { IView } from 'vs/base/browser/ui/grid/gridview'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { return { element: view.element, maximumSize: view.maximumWidth, minimumSize: view.minimumWidth, - onDidChange: mapEvent(view.onDidChange, widthAndHeight => widthAndHeight ? widthAndHeight.width : 0), + onDidChange: mapEvent(view.onDidChange, widthAndHeight => widthAndHeight && widthAndHeight.width), layout: size => view.layout(size, getHeight()) }; } +export interface CenteredViewState { + leftMarginRatio: number; + rightMarginRatio: number; +} + +const GOLDEN_RATIO = { + leftMarginRatio: 0.1909, + rightMarginRatio: 0.1909 +}; + export class CenteredViewLayout { private splitView: SplitView; private element: HTMLElement; - private height: number; + private width: number = 0; + private height: number = 0; private style: ISplitViewStyles; + private didLayout = false; + private splitViewDisposable: IDisposable[] = []; - constructor(private container: HTMLElement, private view: IView) { + constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = GOLDEN_RATIO) { this.container.appendChild(this.view.element); } layout(width: number, height: number): void { + this.width = width; this.height = height; if (this.splitView) { this.splitView.layout(width); + if (!this.didLayout) { + this.splitView.resizeView(0, this.state.leftMarginRatio * this.width); + this.splitView.resizeView(2, this.state.rightMarginRatio * this.width); + } } else { this.view.layout(width, height); } + this.didLayout = true; } isActive(): boolean { @@ -54,8 +74,9 @@ export class CenteredViewLayout { resetView(view: IView): void { this.view = view; if (this.splitView) { + const size = this.splitView.getViewSize(1); this.splitView.removeView(1); - this.splitView.addView(toSplitViewView(this.view, () => this.height), Sizing.Distribute, 1); + this.splitView.addView(toSplitViewView(this.view, () => this.height), size, 1); this.splitView.distributeViewSizes(); } } @@ -75,18 +96,27 @@ export class CenteredViewLayout { styles: this.style }); + const onDidSizesChange = anyEvent(this.splitView.onDidSashChange, this.splitView.onDidSashReset); + this.splitViewDisposable.push(onDidSizesChange(() => { + this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width; + this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width; + })); + + this.splitView.layout(this.width); + const getEmptyView = () => ({ element: $('.centered-layout-margin'), layout: () => undefined, - minimumSize: 20, + minimumSize: 40, maximumSize: Number.POSITIVE_INFINITY, onDidChange: Event.None }); - this.splitView.addView(getEmptyView(), Sizing.Distribute); - this.splitView.addView(toSplitViewView(this.view, () => this.height), Sizing.Distribute); - this.splitView.addView(getEmptyView(), Sizing.Distribute); + this.splitView.addView(toSplitViewView(this.view, () => this.height), 0); + this.splitView.addView(getEmptyView(), this.state.leftMarginRatio * this.width, 0); + this.splitView.addView(getEmptyView(), this.state.rightMarginRatio * this.width, 2); } else { + this.splitViewDisposable = dispose(this.splitViewDisposable); this.splitView.dispose(); this.splitView = undefined; this.container.removeChild(this.element); diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index f43f1221db3df..53be612944931 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -48,6 +48,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor _serviceBrand: any; private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.state'; + private static readonly EDITOR_PART_CENTERED_VIEW_STORAGE_KEY = 'editorpart.centeredview'; //#region Events @@ -79,6 +80,8 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor private _preferredSize: Dimension; private memento: object; + private globalMemento: object; + private _partOptions: IEditorPartOptions; private _activeGroup: IEditorGroupView; @@ -109,6 +112,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this._partOptions = getEditorPartOptions(this.configurationService.getValue()); this.memento = this.getMemento(this.storageService, Scope.WORKSPACE); + this.globalMemento = this.getMemento(this.storageService, Scope.GLOBAL); this._whenRestored = new TPromise(resolve => { this.whenRestoredComplete = resolve; @@ -716,7 +720,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Grid control with center layout this.doCreateGridControl(); - this.centeredViewLayout = new CenteredViewLayout(this.container, this.getGridAsView()); + this.centeredViewLayout = new CenteredViewLayout(this.container, this.getGridAsView(), this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]); // Drop support this._register(this.instantiationService.createInstance(EditorDropTarget, this, this.container)); @@ -1020,6 +1024,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = uiState; } } + this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = this.centeredViewLayout.state; // Forward to all groups this.groupViews.forEach(group => group.shutdown()); From 92171d8cd4e3d27c656d771d8b3f03598388a5a2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 11:11:15 +0200 Subject: [PATCH 035/149] grid - some centered layout cleanup --- .../ui/centered/centeredViewLayout.css | 2 +- .../browser/parts/editor/baseEditor.ts | 7 ------ .../browser/parts/editor/binaryEditor.ts | 4 ---- .../browser/parts/editor/editorPart.ts | 23 +++++++++++-------- .../parts/editor/media/titlecontrol.css | 5 ---- .../browser/parts/editor/sideBySideEditor.ts | 4 ---- .../browser/parts/editor/textDiffEditor.ts | 4 ---- .../preferences/browser/preferencesEditor.ts | 8 ------- 8 files changed, 14 insertions(+), 43 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.css b/src/vs/base/browser/ui/centered/centeredViewLayout.css index 854530732a0bb..f7ee1e9083201 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.css +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.css @@ -3,6 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench > .part.editor > .content .centered-view-layout { +.centered-view-layout { height: 100%; } diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index 8e8d1e5d97eca..040f588de895f 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -143,13 +143,6 @@ export abstract class BaseEditor extends Panel implements IEditor { this._group = group; } - /** - * Subclasses can set this to false if it does not make sense to center editor input. - */ - supportsCenteredLayout(): boolean { - return true; - } - protected getEditorMemento(storageService: IStorageService, editorGroupService: IEditorGroupsService, key: string, limit: number = 10): IEditorMemento { const mementoKey = `${this.getId()}${key}`; diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index 47de56f97916b..7a66f852922a7 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -116,10 +116,6 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { return this.metadata; } - public supportsCenteredLayout(): boolean { - return false; - } - public clearInput(): void { // Clear Meta diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 53be612944931..aed922af57f93 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -89,13 +89,13 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor private mostRecentActiveGroups: GroupIdentifier[] = []; private container: HTMLElement; + private centeredLayoutWidget: CenteredViewLayout; private gridWidget: SerializableGrid; private _whenRestored: TPromise; private whenRestoredComplete: TValueCallback; private previousUIState: IEditorPartUIState; - private centeredViewLayout: CenteredViewLayout; constructor( id: string, @@ -708,7 +708,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor const separatorBorderStyle = { separatorBorder: this.gridSeparatorBorder }; this.gridWidget.style(separatorBorderStyle); - this.centeredViewLayout.styles(separatorBorderStyle); + this.centeredLayoutWidget.styles(separatorBorderStyle); } createContentArea(parent: HTMLElement): HTMLElement { @@ -720,7 +720,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Grid control with center layout this.doCreateGridControl(); - this.centeredViewLayout = new CenteredViewLayout(this.container, this.getGridAsView(), this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]); + this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.getGridAsView(), this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY])); // Drop support this._register(this.instantiationService.createInstance(EditorDropTarget, this, this.container)); @@ -741,11 +741,11 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor } centerLayout(active: boolean): void { - this.centeredViewLayout.activate(active); + this.centeredLayoutWidget.activate(active); } isLayoutCentered(): boolean { - return this.centeredViewLayout.isActive(); + return this.centeredLayoutWidget.isActive(); } private doCreateGridControl(): void { @@ -801,6 +801,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor } private doCreateGridControlWithState(serializedGrid: ISerializedGrid, activeGroupId: GroupIdentifier, editorGroupViewsToReuse?: IEditorGroupView[]): void { + // Determine group views to reuse if any let reuseGroupViews: IEditorGroupView[]; if (editorGroupViewsToReuse) { @@ -839,9 +840,10 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this.gridWidget = gridWidget; if (gridWidget) { - if (this.centeredViewLayout) { - this.centeredViewLayout.resetView(this.getGridAsView()); + if (this.centeredLayoutWidget) { + this.centeredLayoutWidget.resetView(this.getGridAsView()); } + this._onDidSizeConstraintsChange.input = gridWidget.onDidChange; } @@ -999,7 +1001,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Layout Grid try { - this.centeredViewLayout.layout(this.dimension.width, this.dimension.height); + this.centeredLayoutWidget.layout(this.dimension.width, this.dimension.height); } catch (error) { this.gridError(error); } @@ -1024,7 +1026,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] = uiState; } } - this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = this.centeredViewLayout.state; + + // Persist centered view state + this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = this.centeredLayoutWidget.state; // Forward to all groups this.groupViews.forEach(group => group.shutdown()); @@ -1042,7 +1046,6 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor if (this.gridWidget) { this.gridWidget.dispose(); } - this.centeredViewLayout.dispose(); super.dispose(); } diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 79479a2046f65..da2df0dfecace 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -11,11 +11,6 @@ flex: 1; } -.monaco-workbench > .part.editor > .content .editor-group-container.centered > .title .title-label { - flex-direction: row; - justify-content: center; -} - .monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label a, .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a { text-decoration: none; diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 21922b1f46f53..48230a73d7bfa 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -151,10 +151,6 @@ export class SideBySideEditor extends BaseEditor { return this.detailsEditor; } - supportsCenteredLayout(): boolean { - return false; - } - private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Thenable { if (!newInput.matches(oldInput)) { if (oldInput) { diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 22c0a7008c31e..74753bc36cf05 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -163,10 +163,6 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { } } - public supportsCenteredLayout(): boolean { - return false; - } - private restoreTextDiffEditorViewState(input: EditorInput): boolean { if (input instanceof DiffEditorInput) { const resource = this.toDiffEditorViewStateResource(input); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index aa23dcd4ca1bb..0eb68416f9403 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -205,10 +205,6 @@ export class PreferencesEditor extends BaseEditor { super.clearInput(); } - public supportsCenteredLayout(): boolean { - return false; - } - protected setEditorVisible(visible: boolean, group: IEditorGroup): void { this.sideBySidePreferencesWidget.setEditorVisible(visible, group); super.setEditorVisible(visible, group); @@ -1053,10 +1049,6 @@ export class DefaultPreferencesEditor extends BaseTextEditor { this.getControl().layout(dimension); } - public supportsCenteredLayout(): boolean { - return false; - } - protected getAriaLabel(): string { return nls.localize('preferencesAriaLabel', "Default preferences. Readonly text editor."); } From ce930c32c78ba0b33213de3ec7630bda08644306 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Jun 2018 11:14:54 +0200 Subject: [PATCH 036/149] centered view: no need to distribute view sizes --- src/vs/base/browser/ui/centered/centeredViewLayout.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index c3c97743d8b38..aefa4364fb6bd 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -77,7 +77,6 @@ export class CenteredViewLayout { const size = this.splitView.getViewSize(1); this.splitView.removeView(1); this.splitView.addView(toSplitViewView(this.view, () => this.height), size, 1); - this.splitView.distributeViewSizes(); } } From b343035fd51f16d1185afc63bd29228664c5e564 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 11:30:05 +0200 Subject: [PATCH 037/149] fix #52095 --- .../browser/parts/editor/editorDropTarget.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 96965c2979d50..f5092c4881ddd 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -343,16 +343,19 @@ class DropOverlay extends Themable { } private doPositionOverlay(options: { top: string, left: string, width: string, height: string }): void { - this.overlay.style.top = options.top; - this.overlay.style.left = options.left; - this.overlay.style.width = options.width; + // Container const offsetHeight = this.getOverlayOffsetHeight(); if (offsetHeight) { - this.overlay.style.height = `calc(${options.height} - ${offsetHeight}px)`; + this.container.style.height = `calc(100% - ${offsetHeight}px)`; } else { - this.overlay.style.height = options.height; + this.container.style.height = '100%'; } + + // Overlay + this.overlay.style.top = options.top; + this.overlay.style.left = options.left; + this.overlay.style.width = options.width; } private getOverlayOffsetHeight(): number { From 065c9497bd5ad7a57472d157913781eaa5543b2d Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Jun 2018 11:46:26 +0200 Subject: [PATCH 038/149] centered view: double click to reset size --- .../browser/ui/centered/centeredViewLayout.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index aefa4364fb6bd..2a0e06a826f73 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -7,7 +7,7 @@ import 'vs/css!./centeredViewLayout'; import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; -import { Event, mapEvent, anyEvent } from 'vs/base/common/event'; +import { Event, mapEvent } from 'vs/base/common/event'; import { IView } from 'vs/base/browser/ui/grid/gridview'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -51,8 +51,7 @@ export class CenteredViewLayout { if (this.splitView) { this.splitView.layout(width); if (!this.didLayout) { - this.splitView.resizeView(0, this.state.leftMarginRatio * this.width); - this.splitView.resizeView(2, this.state.rightMarginRatio * this.width); + this.resizeMargins(); } } else { this.view.layout(width, height); @@ -60,6 +59,11 @@ export class CenteredViewLayout { this.didLayout = true; } + private resizeMargins(): void { + this.splitView.resizeView(0, this.state.leftMarginRatio * this.width); + this.splitView.resizeView(2, this.state.rightMarginRatio * this.width); + } + isActive(): boolean { return !!this.splitView; } @@ -95,18 +99,22 @@ export class CenteredViewLayout { styles: this.style }); - const onDidSizesChange = anyEvent(this.splitView.onDidSashChange, this.splitView.onDidSashReset); - this.splitViewDisposable.push(onDidSizesChange(() => { + this.splitViewDisposable.push(this.splitView.onDidSashChange(() => { this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width; this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width; })); + this.splitViewDisposable.push(this.splitView.onDidSashReset(() => { + this.state.leftMarginRatio = GOLDEN_RATIO.leftMarginRatio; + this.state.rightMarginRatio = GOLDEN_RATIO.rightMarginRatio; + this.resizeMargins(); + })); this.splitView.layout(this.width); const getEmptyView = () => ({ element: $('.centered-layout-margin'), layout: () => undefined, - minimumSize: 40, + minimumSize: 60, maximumSize: Number.POSITIVE_INFINITY, onDidChange: Event.None }); From 1ce43ea94fcded685f5095030f25b87ceec84363 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Jun 2018 11:47:57 +0200 Subject: [PATCH 039/149] Use fsPath if scheme is file --- src/vs/platform/extensionManagement/node/extensionLifecycle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 2ce7fce67fdf3..170ec07d73243 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -43,7 +43,7 @@ export class ExtensionsLifecycle extends Disposable { this.logService.warn(extension.identifier.id, 'Uninstall script should be a node script'); return null; } - return { uninstallHook: join(extension.location.path, uninstallScript[1]), args: uninstallScript.slice(2) || [] }; + return { uninstallHook: join(extension.location.fsPath, uninstallScript[1]), args: uninstallScript.slice(2) || [] }; } return null; } From e909a0d91d5377d35aa15ae51d3e368dbf0d5d6c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 11:56:26 +0200 Subject: [PATCH 040/149] fix #52201 --- .../feedback/electron-browser/feedback.ts | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts index 08a18f96c24ab..e2623a7b857da 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts @@ -139,8 +139,12 @@ export class FeedbackDropdown extends Dropdown { $('h2.title').text(nls.localize("label.sendASmile", "Tweet us your feedback.")).appendTo($form); - const cancelBtn = $('div.cancel').attr('tabindex', '0'); - cancelBtn.on(dom.EventType.MOUSE_OVER, () => { + const closeBtn = $('div.cancel').attr({ + 'tabindex': '0', + 'role': 'button', + 'title': nls.localize('close', "Close") + }); + closeBtn.on(dom.EventType.MOUSE_OVER, () => { const theme = this.themeService.getTheme(); let darkenFactor: number; switch (theme.type) { @@ -153,13 +157,13 @@ export class FeedbackDropdown extends Dropdown { } if (darkenFactor) { - cancelBtn.getHTMLElement().style.backgroundColor = darken(theme.getColor(editorWidgetBackground), darkenFactor)(theme).toString(); + closeBtn.getHTMLElement().style.backgroundColor = darken(theme.getColor(editorWidgetBackground), darkenFactor)(theme).toString(); } }); - cancelBtn.on(dom.EventType.MOUSE_OUT, () => { - cancelBtn.getHTMLElement().style.backgroundColor = null; + closeBtn.on(dom.EventType.MOUSE_OUT, () => { + closeBtn.getHTMLElement().style.backgroundColor = null; }); - this.invoke(cancelBtn, () => { + this.invoke(closeBtn, () => { this.hide(); }).appendTo($form); @@ -178,7 +182,8 @@ export class FeedbackDropdown extends Dropdown { this.smileyInput = $('div').addClass('sentiment smile').attr({ 'aria-checked': 'false', - 'aria-label': nls.localize('smileCaption', "Happy"), + 'aria-label': nls.localize('smileCaption', "Happy Feedback Sentiment"), + 'title': nls.localize('smileCaption', "Happy Feedback Sentiment"), 'tabindex': 0, 'role': 'checkbox' }); @@ -186,7 +191,8 @@ export class FeedbackDropdown extends Dropdown { this.frownyInput = $('div').addClass('sentiment frown').attr({ 'aria-checked': 'false', - 'aria-label': nls.localize('frownCaption', "Sad"), + 'aria-label': nls.localize('frownCaption', "Sad Feedback Sentiment"), + 'title': nls.localize('frownCaption', "Sad Feedback Sentiment"), 'tabindex': 0, 'role': 'checkbox' }); @@ -254,6 +260,7 @@ export class FeedbackDropdown extends Dropdown { this.sendButton.label = nls.localize('tweet', "Tweet"); this.$sendButton = new Builder(this.sendButton.element); this.$sendButton.addClass('send'); + this.$sendButton.title(nls.localize('tweetFeedback', "Tweet Feedback")); this.toDispose.push(attachButtonStyler(this.sendButton, this.themeService)); this.sendButton.onDidClick(() => { From 2afaa0d682f1015832187f793e5c66ac25a834c9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 11:59:17 +0200 Subject: [PATCH 041/149] fix #52208 --- src/vs/workbench/parts/feedback/electron-browser/feedback.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts index e2623a7b857da..c9b4a6a03bc5b 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts @@ -239,7 +239,7 @@ export class FeedbackDropdown extends Dropdown { this.feedbackDescriptionInput = $('textarea.feedback-description').attr({ rows: 3, maxlength: this.maxFeedbackCharacters, - 'aria-label': nls.localize("commentsHeader", "Comments") + 'aria-label': nls.localize("feedbackTextInput", "Tell us your feedback") }) .text(this.feedback).attr('required', 'required') .on('keyup', () => { From c361c4a31c591984f33b502107befff6e5f68f6a Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 18 Jun 2018 17:41:38 +0200 Subject: [PATCH 042/149] Back button constant (#49340) --- .../platform/quickinput/common/quickInput.ts | 4 ++ src/vs/vscode.proposed.d.ts | 6 ++- .../electron-browser/mainThreadQuickOpen.ts | 18 ++++++--- src/vs/workbench/api/node/extHost.api.impl.ts | 5 +++ src/vs/workbench/api/node/extHostQuickOpen.ts | 9 ++++- .../parts/quickinput/media/dark/back.svg | 1 + .../parts/quickinput/media/light/back.svg | 1 + .../quickinput/quickInput.contribution.ts | 10 ++++- .../browser/parts/quickinput/quickInput.ts | 39 +++++++++++++++++++ 9 files changed, 82 insertions(+), 11 deletions(-) create mode 100755 src/vs/workbench/browser/parts/quickinput/media/dark/back.svg create mode 100755 src/vs/workbench/browser/parts/quickinput/media/light/back.svg diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index acfda318d5ea1..ae0f47d225fdf 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -184,6 +184,8 @@ export interface IQuickInputService { */ input(options?: IInputOptions, token?: CancellationToken): TPromise; + backButton: IQuickInputButton; + createQuickPick(): IQuickPick; createInputBox(): IInputBox; @@ -195,5 +197,7 @@ export interface IQuickInputService { accept(): TPromise; + back(): TPromise; + cancel(): TPromise; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 44c776e8f4ebb..69c74a17ebb59 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -523,6 +523,8 @@ declare module 'vscode' { export namespace window { + export const quickInputBackButton: QuickInputButton; + /** * Implementation incomplete. See #49340. */ @@ -610,8 +612,8 @@ declare module 'vscode' { } export interface QuickInputButton { - iconPath: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; - tooltip?: string | undefined; + readonly iconPath: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; + readonly tooltip?: string | undefined; } //#endregion diff --git a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts index 0430d5a27dbed..1afbe3b936ed1 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts @@ -170,14 +170,20 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { session.hide(); } } else if (param === 'buttons') { - params.buttons.forEach(button => { - const iconPath = button.iconPath; - iconPath.dark = URI.revive(iconPath.dark); - if (iconPath.light) { - iconPath.light = URI.revive(iconPath.light); + session[param] = params.buttons.map(button => { + if (button.handle === -1) { + return this._quickInputService.backButton; } + const { iconPath, tooltip, handle } = button; + return { + iconPath: { + dark: URI.revive(iconPath.dark), + light: iconPath.light && URI.revive(iconPath.light) + }, + tooltip, + handle + }; }); - session[param] = params[param]; } else { session[param] = params[param]; } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 64c5d86975620..5bf7319a8325f 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -455,6 +455,11 @@ export function createApiFactory( registerProtocolHandler: proposedApiFunction(extension, (handler: vscode.ProtocolHandler) => { return extHostUrls.registerProtocolHandler(extension.id, handler); }), + get quickInputBackButton() { + return proposedApiFunction(extension, (): vscode.QuickInputButton => { + return extHostQuickOpen.backButton; + })(); + }, createQuickPick: proposedApiFunction(extension, (): vscode.QuickPick => { return extHostQuickOpen.createQuickPick(extension.id); }), diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index a3445ad123958..b7fd0b53aa09c 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -16,6 +16,8 @@ import { ExtHostQuickOpenShape, IMainContext, MainContext, MainThreadQuickOpenSh import URI from 'vs/base/common/uri'; import { ThemeIcon } from 'vs/workbench/api/node/extHostTypes'; +const backButton: QuickInputButton = { iconPath: 'back.svg' }; + export type Item = string | QuickPickItem; export class ExtHostQuickOpen implements ExtHostQuickOpenShape { @@ -151,6 +153,8 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // ---- QuickInput + backButton = backButton; + createQuickPick(extensionId: string): QuickPick { const session = new ExtHostQuickPick(this._proxy, extensionId, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); @@ -324,13 +328,14 @@ class ExtHostQuickInput implements QuickInput { this._buttons = buttons; this._handlesToButtons.clear(); buttons.forEach((button, i) => { - this._handlesToButtons.set(i, button); + const handle = button === backButton ? -1 : i; + this._handlesToButtons.set(handle, button); }); this.update({ buttons: buttons.map((button, i) => ({ iconPath: getIconUris(button.iconPath), tooltip: button.tooltip, - handle: i, + handle: button === backButton ? -1 : i, })) }); } diff --git a/src/vs/workbench/browser/parts/quickinput/media/dark/back.svg b/src/vs/workbench/browser/parts/quickinput/media/dark/back.svg new file mode 100755 index 0000000000000..c5c4f472b4d7a --- /dev/null +++ b/src/vs/workbench/browser/parts/quickinput/media/dark/back.svg @@ -0,0 +1 @@ +CollapseChevronLeft_md_16x \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/quickinput/media/light/back.svg b/src/vs/workbench/browser/parts/quickinput/media/light/back.svg new file mode 100755 index 0000000000000..324ab15b1ec41 --- /dev/null +++ b/src/vs/workbench/browser/parts/quickinput/media/light/back.svg @@ -0,0 +1 @@ +CollapseChevronLeft_md_16x \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts index 5aea4a4cfce05..cc7fb7c75777f 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts @@ -4,7 +4,15 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { QuickPickManyToggle } from 'vs/workbench/browser/parts/quickinput/quickInput'; +import { QuickPickManyToggle, BackAction } from 'vs/workbench/browser/parts/quickinput/quickInput'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen'; KeybindingsRegistry.registerCommandAndKeybindingRule(QuickPickManyToggle); + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(BackAction, BackAction.ID, BackAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }, inQuickOpenContext, KeybindingsRegistry.WEIGHT.workbenchContrib(50)), 'Back'); diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 9ba6faf423c8f..83529563f38de 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -54,6 +54,7 @@ interface QuickInputUI { progressBar: ProgressBar; list: QuickInputList; onDidAccept: Event; + onDidTriggerButton: Event; ignoreFocusOut: boolean; show(controller: QuickInput): void; setVisibilities(visibilities: Visibilities): void; @@ -166,6 +167,13 @@ class QuickInput implements IQuickInput { if (this.visible) { return; } + this.disposables.push( + this.ui.onDidTriggerButton(button => { + if (this.buttons.indexOf(button) !== -1) { + this.onDidTriggerButtonEmitter.fire(button); + } + }), + ); this.ui.show(this); this.visible = true; this.update(); @@ -642,6 +650,7 @@ export class QuickInputService extends Component implements IQuickInputService { private inQuickOpenWidgets: Record = {}; private inQuickOpenContext: IContextKey; private onDidAcceptEmitter = new Emitter(); + private onDidTriggerButtonEmitter = new Emitter(); private controller: QuickInput; @@ -806,6 +815,7 @@ export class QuickInputService extends Component implements IQuickInputService { progressBar, list, onDidAccept: this.onDidAcceptEmitter.event, + onDidTriggerButton: this.onDidTriggerButtonEmitter.event, ignoreFocusOut: false, show: controller => this.show(controller), hide: () => this.hide(), @@ -928,6 +938,15 @@ export class QuickInputService extends Component implements IQuickInputService { }); } + backButton = { + iconPath: { + dark: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/dark/back.svg')), + light: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/light/back.svg')) + }, + tooltip: localize('quickInput.back', "Back"), + handle: -1 // TODO + }; + createQuickPick(): IQuickPick { this.create(); return new QuickPick(this.ui); @@ -1034,6 +1053,11 @@ export class QuickInputService extends Component implements IQuickInputService { return TPromise.as(undefined); } + back() { + this.onDidTriggerButtonEmitter.fire(this.backButton); + return TPromise.as(undefined); + } + cancel() { this.hide(); return TPromise.as(undefined); @@ -1111,3 +1135,18 @@ export const QuickPickManyToggle: ICommandAndKeybindingRule = { quickInputService.toggle(); } }; + +export class BackAction extends Action { + + public static readonly ID = 'workbench.action.quickInputBack'; + public static readonly LABEL = localize('back', "Back"); + + constructor(id: string, label: string, @IQuickInputService private quickInputService: IQuickInputService) { + super(id, label); + } + + public run(): TPromise { + this.quickInputService.back(); + return TPromise.as(null); + } +} From 48d38a13b1de0a44e87e47b72bbde7ce84a929ae Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Jun 2018 12:15:33 +0200 Subject: [PATCH 043/149] fixes #52156 --- src/vs/base/common/labels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index ab86d970787d9..1fa02234ebc54 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -45,7 +45,7 @@ export function getPathLabel(resource: URI | string, userHomeProvider: IUserHome if (isEqual(baseResource.uri, resource, !isLinux)) { pathLabel = ''; // no label if paths are identical } else { - pathLabel = normalize(ltrim(resource.toString().substr(baseResource.uri.toString().length), nativeSep), true); + pathLabel = normalize(ltrim(resource.toString().substr(baseResource.uri.toString().length), sep), true); } if (hasMultipleRoots) { From a979c738966cb5523ca990103d6df55e8e040709 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 12:23:23 +0200 Subject: [PATCH 044/149] bulk edit provider listens on operation events only, add more test (partity disabled due to #52212), #10659 --- .../src/singlefolder-tests/workspace.test.ts | 97 ++++++++++++++++- extensions/vscode-api-tests/src/utils.ts | 6 +- src/vs/workbench/api/node/extHostTypes.ts | 100 ++++++++++-------- .../electron-browser/bulkEditService.ts | 36 +++---- .../api/extHostLanguageFeatures.test.ts | 6 +- .../electron-browser/api/extHostTypes.test.ts | 4 +- .../api/mainThreadEditors.test.ts | 2 +- 7 files changed, 177 insertions(+), 74 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 373fc9b1f68dc..77ed49227ca1b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -7,9 +7,10 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, deleteFile, closeAllEditors, pathEquals } from '../utils'; -import { join, basename } from 'path'; +import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName } from '../utils'; +import { join, basename, dirname } from 'path'; import * as fs from 'fs'; +import * as os from 'os'; suite('workspace-namespace', () => { @@ -519,7 +520,6 @@ suite('workspace-namespace', () => { }); }); - test('applyEdit should fail when editing deleted resource', async () => { const resource = await createRandomFile(); @@ -564,7 +564,98 @@ suite('workspace-namespace', () => { let success = await vscode.workspace.applyEdit(edit); assert.equal(success, true); + // todo@ben https://github.com/Microsoft/vscode/issues/52212 // let doc = await vscode.workspace.openTextDocument(newUri); // assert.equal(doc.getText(), 'AFTERBEFORE'); }); + + function nameWithUnderscore(uri: vscode.Uri) { + return uri.with({ path: join(dirname(uri.path), `_${basename(uri.path)}`) }); + } + + test('WorkspaceEdit: applying edits before and after rename duplicates resource #42633', async function () { + let docUri = await createRandomFile(); + let newUri = nameWithUnderscore(docUri); + + let we = new vscode.WorkspaceEdit(); + we.insert(docUri, new vscode.Position(0, 0), 'Hello'); + we.insert(docUri, new vscode.Position(0, 0), 'Foo'); + we.renameFile(docUri, newUri); + we.insert(newUri, new vscode.Position(0, 0), 'Bar'); + + assert.ok(await vscode.workspace.applyEdit(we)); + // todo@ben https://github.com/Microsoft/vscode/issues/52212 + // let doc = await vscode.workspace.openTextDocument(newUri); + // assert.equal(doc.getText(), 'BarFooHello'); + }); + + test('WorkspaceEdit: Problem recreating a renamed resource #42634', async function () { + let docUri = await createRandomFile(); + let newUri = nameWithUnderscore(docUri); + + let we = new vscode.WorkspaceEdit(); + we.insert(docUri, new vscode.Position(0, 0), 'Hello'); + we.insert(docUri, new vscode.Position(0, 0), 'Foo'); + we.renameFile(docUri, newUri); + + we.createFile(docUri); + we.insert(docUri, new vscode.Position(0, 0), 'Bar'); + + assert.ok(await vscode.workspace.applyEdit(we)); + + // todo@ben https://github.com/Microsoft/vscode/issues/52212 + // let newDoc = await vscode.workspace.openTextDocument(newUri); + // assert.equal(newDoc.getText(), 'FooHello'); + + // todo@ben https://github.com/Microsoft/vscode/issues/52212 + // let doc = await vscode.workspace.openTextDocument(docUri); + // assert.equal(doc.getText(), 'Bar'); + }); + + test('WorkspaceEdit api - after saving a deleted file, it still shows up as deleted. #42667', async function () { + let docUri = await createRandomFile(); + let we = new vscode.WorkspaceEdit(); + we.deleteFile(docUri); + we.insert(docUri, new vscode.Position(0, 0), 'InsertText'); + + assert.ok(!(await vscode.workspace.applyEdit(we))); + try { + await vscode.workspace.openTextDocument(docUri); + assert.ok(false); + } catch (e) { + assert.ok(true); + } + }); + + test('WorkspaceEdit: edit and rename parent folder duplicates resource #42641', async function () { + + let dir = join(os.tmpdir(), 'before-' + rndName()); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + + let docUri = await createRandomFile('', dir); + let docParent = docUri.with({ path: dirname(docUri.path) }); + let newParent = nameWithUnderscore(docParent); + + let we = new vscode.WorkspaceEdit(); + we.insert(docUri, new vscode.Position(0, 0), 'Hello'); + we.renameFile(docParent, newParent); + + assert.ok(await vscode.workspace.applyEdit(we)); + + try { + await vscode.workspace.openTextDocument(docUri); + assert.ok(false); + } catch (e) { + assert.ok(true); + } + + let newUri = newParent.with({ path: join(newParent.path, basename(docUri.path)) }); + let doc = await vscode.workspace.openTextDocument(newUri); + assert.ok(doc); + + // todo@ben https://github.com/Microsoft/vscode/issues/52212 + // assert.equal(doc.getText(), 'Hello'); + }); }); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index 0d869ed787df7..f2b3511598c38 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -10,13 +10,13 @@ import * as fs from 'fs'; import * as os from 'os'; import { join } from 'path'; -function rndName() { +export function rndName() { return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); } -export function createRandomFile(contents = ''): Thenable { +export function createRandomFile(contents = '', dir: string = os.tmpdir()): Thenable { return new Promise((resolve, reject) => { - const tmpFile = join(os.tmpdir(), rndName()); + const tmpFile = join(dir, rndName()); fs.writeFile(tmpFile, contents, (error) => { if (error) { return reject(error); diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 208a20e8b9601..39658d10c72ae 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -13,6 +13,8 @@ import { isMarkdownString } from 'vs/base/common/htmlContent'; import { IRelativePattern } from 'vs/base/common/glob'; import { relative } from 'path'; import { startsWith } from 'vs/base/common/strings'; +import { values } from 'vs/base/common/map'; +import { coalesce } from 'vs/base/common/arrays'; export class Disposable { @@ -492,12 +494,11 @@ export class TextEdit { } } -export class WorkspaceEdit implements vscode.WorkspaceEdit { - private _seqPool: number = 0; - private _resourceEdits: { seq: number, from: URI, to: URI }[] = []; - private _textEdits = new Map(); +export class WorkspaceEdit implements vscode.WorkspaceEdit { + + private _edits = new Array<{ _type: 1, from: URI, to: URI } | { _type: 2, uri: URI, edit: TextEdit }>(); createFile(uri: vscode.Uri): void { this.renameFile(undefined, uri); @@ -508,22 +509,11 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } renameFile(from: vscode.Uri, to: vscode.Uri): void { - this._resourceEdits.push({ seq: this._seqPool++, from, to }); + this._edits.push({ _type: 1, from, to }); } - // resourceEdits(): [vscode.Uri, vscode.Uri][] { - // return this._resourceEdits.map(({ from, to }) => (<[vscode.Uri, vscode.Uri]>[from, to])); - // } - replace(uri: URI, range: Range, newText: string): void { - let edit = new TextEdit(range, newText); - let array = this.get(uri); - if (array) { - array.push(edit); - } else { - array = [edit]; - } - this.set(uri, array); + this._edits.push({ _type: 2, uri, edit: new TextEdit(range, newText) }); } insert(resource: URI, position: Position, newText: string): void { @@ -535,54 +525,76 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } has(uri: URI): boolean { - return this._textEdits.has(uri.toString()); + for (const edit of this._edits) { + if (edit._type === 2 && edit.uri.toString() === uri.toString()) { + return true; + } + } + return false; } set(uri: URI, edits: TextEdit[]): void { - let data = this._textEdits.get(uri.toString()); - if (!data) { - data = { seq: this._seqPool++, uri, edits: [] }; - this._textEdits.set(uri.toString(), data); - } if (!edits) { - data.edits = undefined; + // remove all text edits for `uri` + for (let i = 0; i < this._edits.length; i++) { + const element = this._edits[i]; + if (element._type === 2 && element.uri.toString() === uri.toString()) { + this._edits[i] = undefined; + } + } + this._edits = coalesce(this._edits); } else { - data.edits = edits.slice(0); + // append edit to the end + for (const edit of edits) { + if (edit) { + this._edits.push({ _type: 2, uri, edit }); + } + } } } get(uri: URI): TextEdit[] { - if (!this._textEdits.has(uri.toString())) { + let res: TextEdit[] = []; + for (let candidate of this._edits) { + if (candidate._type === 2 && candidate.uri.toString() === uri.toString()) { + res.push(candidate.edit); + } + } + if (res.length === 0) { return undefined; } - const { edits } = this._textEdits.get(uri.toString()); - return edits ? edits.slice() : undefined; + return res; } entries(): [URI, TextEdit[]][] { - const res: [URI, TextEdit[]][] = []; - this._textEdits.forEach(value => res.push([value.uri, value.edits])); - return res.slice(); + let textEdits = new Map(); + for (let candidate of this._edits) { + if (candidate._type === 2) { + let textEdit = textEdits.get(candidate.uri.toString()); + if (!textEdit) { + textEdit = [candidate.uri, []]; + textEdits.set(candidate.uri.toString(), textEdit); + } + textEdit[1].push(candidate.edit); + } + } + return values(textEdits); } allEntries(): ([URI, TextEdit[]] | [URI, URI])[] { - // use the 'seq' the we have assigned when inserting - // the operation and use that order in the resulting - // array - const res: ([URI, TextEdit[]] | [URI, URI])[] = []; - this._textEdits.forEach(value => { - const { seq, uri, edits } = value; - res[seq] = [uri, edits]; - }); - this._resourceEdits.forEach(value => { - const { seq, from, to } = value; - res[seq] = [from, to]; - }); + let res: ([URI, TextEdit[]] | [URI, URI])[] = []; + for (let edit of this._edits) { + if (edit._type === 1) { + res.push([edit.from, edit.to]); + } else { + res.push([edit.uri, [edit.edit]]); + } + } return res; } get size(): number { - return this._textEdits.size + this._resourceEdits.length; + return this.entries().length; } toJSON(): any { diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index 3e6906377e776..f104e52bc7704 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -6,7 +6,7 @@ import { getPathLabel } from 'vs/base/common/labels'; -import { IDisposable, IReference, dispose } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -15,36 +15,30 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; -import { ResourceFileEdit, ResourceTextEdit, WorkspaceEdit, isResourceFileEdit, isResourceTextEdit } from 'vs/editor/common/modes'; +import { isResourceFileEdit, isResourceTextEdit, ResourceFileEdit, ResourceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { localize } from 'vs/nls'; -import { FileChangeType, IFileService } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IProgress, IProgressRunner, emptyProgressRunner } from 'vs/platform/progress/common/progress'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { emptyProgressRunner, IProgress, IProgressRunner } from 'vs/platform/progress/common/progress'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { ILogService } from 'vs/platform/log/common/log'; abstract class Recording { static start(fileService: IFileService): Recording { let _changes = new Set(); - let stop: IDisposable; - - stop = fileService.onFileChanges(event => { - for (const change of event.changes) { - if (change.type === FileChangeType.UPDATED) { - _changes.add(change.resource.toString()); - } - } + let subscription = fileService.onAfterOperation(e => { + _changes.add(e.resource.toString()); }); return { - stop() { return dispose(stop); }, + stop() { return subscription.dispose(); }, hasChanged(resource) { return _changes.has(resource.toString()); } }; } @@ -269,6 +263,7 @@ export class BulkEdit { constructor( editor: ICodeEditor, progress: IProgressRunner, + @ILogService private readonly _logService: ILogService, @ITextModelService private readonly _textModelService: ITextModelService, @IFileService private readonly _fileService: IFileService, @ITextFileService private readonly _textFileService: ITextFileService, @@ -343,8 +338,8 @@ export class BulkEdit { } private async _performFileEdits(edits: ResourceFileEdit[], progress: IProgress) { + this._logService.debug('_performFileEdits', JSON.stringify(edits)); for (const edit of edits) { - progress.report(undefined); if (edit.newUri && edit.oldUri) { @@ -358,6 +353,7 @@ export class BulkEdit { } private async _performTextEdits(edits: ResourceTextEdit[], progress: IProgress): TPromise { + this._logService.debug('_performTextEdits', JSON.stringify(edits)); const recording = Recording.start(this._fileService); const model = new BulkEditModel(this._textModelService, this._editor, edits, progress); @@ -424,14 +420,16 @@ export class BulkEditService implements IBulkEditService { } } - const bulkEdit = new BulkEdit(options.editor, options.progress, this._textModelService, this._fileService, this._textFileService, this._environmentService, this._contextService); + const bulkEdit = new BulkEdit(options.editor, options.progress, this._logService, this._textModelService, this._fileService, this._textFileService, this._environmentService, this._contextService); bulkEdit.add(edits); return bulkEdit.perform().then(selection => { return { selection, ariaSummary: bulkEdit.ariaMessage() }; }, err => { + // console.log('apply FAILED'); + // console.log(err); this._logService.error(err); - return TPromise.wrapError(err); + throw err; }); } } diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 201206f9c0dd2..f5479d67f9eda 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -841,8 +841,10 @@ suite('ExtHostLanguageFeatures', function () { return rpcProtocol.sync().then(() => { return rename(model, new EditorPosition(1, 1), 'newName').then(value => { - assert.equal(value.edits.length, 1); // least relevant renamer - assert.equal((value.edits)[0].edits.length, 2); // least relevant renamer + // least relevant rename provider + assert.equal(value.edits.length, 2); + assert.equal((value.edits[0]).edits.length, 1); + assert.equal((value.edits[1]).edits.length, 1); }); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index 316a1e3a3ccdf..9c868b31e5e16 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -368,8 +368,8 @@ suite('ExtHostTypes', function () { ]); edit.set(b, undefined); - assert.ok(edit.has(b)); - assert.equal(edit.size, 2); + assert.ok(!edit.has(b)); + assert.equal(edit.size, 1); edit.set(b, [types.TextEdit.insert(new types.Position(0, 0), 'ffff')]); assert.equal(edit.get(b).length, 1); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index 0150026110ff9..7277d63ccc5b2 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -75,7 +75,7 @@ suite('MainThreadEditors', () => { const workbenchEditorService = new TestEditorService(); const editorGroupService = new TestEditorGroupsService(); - const bulkEditService = new BulkEditService(NullLogService as any, modelService, new TestEditorService(), null, fileService, textFileService, TestEnvironmentService, new TestContextService()); + const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), null, fileService, textFileService, TestEnvironmentService, new TestContextService()); const rpcProtocol = new TestRPCProtocol(); rpcProtocol.set(ExtHostContext.ExtHostDocuments, new class extends mock() { From f4df42f11e1e8c79517177ffc5e23228d0f59215 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Jun 2018 13:06:54 +0200 Subject: [PATCH 045/149] Fix #51234 --- .../parts/activitybar/activitybarPart.ts | 11 +++-- .../parts/compositebar/compositeBarActions.ts | 43 ++++++++++++++----- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index e323439ee9a42..5de79ae5dade3 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -306,10 +306,10 @@ export class ActivitybarPart extends Part { private enableCompositeActions(viewlet: ViewletDescriptor): void { const { activityAction, pinnedAction } = this.getCompositeActions(viewlet.id); if (activityAction instanceof PlaceHolderViewletActivityAction) { - activityAction.enable(viewlet); + activityAction.setActivity(viewlet); } if (pinnedAction instanceof PlaceHolderToggleCompositePinnedAction) { - pinnedAction.enable(viewlet); + pinnedAction.setActivity(viewlet); } } @@ -376,9 +376,8 @@ class PlaceHolderViewletActivityAction extends ViewletActivityAction { this.enabled = false; } - enable(activity: IActivity): void { - this.label = activity.name; - this.class = activity.cssClass; + setActivity(activity: IActivity): void { + this.activity = activity; this.enabled = true; } @@ -393,7 +392,7 @@ class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinnedAction this.enabled = false; } - enable(activity: IActivity): void { + setActivity(activity: IActivity): void { this.label = activity.name; this.enabled = true; } diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts index 2da7b86088f41..7beeddb156ca5 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts @@ -54,6 +54,7 @@ export interface ICompositeBar { export class ActivityAction extends Action { private badge: IBadge; private clazz: string | undefined; + private _onDidChangeActivity = new Emitter(); private _onDidChangeBadge = new Emitter(); constructor(private _activity: IActivity) { @@ -66,6 +67,15 @@ export class ActivityAction extends Action { return this._activity; } + public set activity(activity: IActivity) { + this._activity = activity; + this._onDidChangeActivity.fire(this); + } + + public get onDidChangeActivity(): Event { + return this._onDidChangeActivity.event; + } + public get onDidChangeBadge(): Event { return this._onDidChangeBadge.event; } @@ -127,6 +137,7 @@ export class ActivityActionItem extends BaseActionItem { super(null, action, options); this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose); + action.onDidChangeActivity(this.updateActivity, this, this._callOnDispose); action.onDidChangeBadge(this.updateBadge, this, this._callOnDispose); } @@ -165,8 +176,7 @@ export class ActivityActionItem extends BaseActionItem { // Make the container tab-able for keyboard navigation this.$container = $(container).attr({ tabIndex: '0', - role: 'button', - title: this.activity.name + role: 'button' }); // Try hard to prevent keyboard only focus feedback when using mouse @@ -186,27 +196,26 @@ export class ActivityActionItem extends BaseActionItem { // Label this.$label = $('a.action-label').appendTo(this.builder); - if (this.activity.cssClass) { - this.$label.addClass(this.activity.cssClass); - } - if (!this.options.icon) { - this.$label.text(this.getAction().label); - } this.$badge = this.builder.clone().div({ 'class': 'badge' }, badge => { this.$badgeContent = badge.div({ 'class': 'badge-content' }); }); - this.$badge.hide(); + this.updateActivity(); this.updateStyles(); - this.updateBadge(); } private onThemeChange(theme: ITheme): void { this.updateStyles(); } + protected updateActivity(): void { + this.updateLabel(); + this.updateTitle(this.activity.name); + this.updateBadge(); + } + protected updateBadge(): void { const action = this.getAction(); if (!this.$badge || !this.$badgeContent || !(action instanceof ActivityAction)) { @@ -271,7 +280,19 @@ export class ActivityActionItem extends BaseActionItem { } else { title = this.activity.name; } + this.updateTitle(title); + } + + private updateLabel(): void { + if (this.activity.cssClass) { + this.$label.addClass(this.activity.cssClass); + } + if (!this.options.icon) { + this.$label.text(this.getAction().label); + } + } + private updateTitle(title: string): void { [this.$label, this.$badge, this.$container].forEach(b => { if (b) { b.attr('aria-label', title); @@ -409,6 +430,8 @@ export class CompositeActionItem extends ActivityActionItem { if (!CompositeActionItem.manageExtensionAction) { CompositeActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction); } + + compositeActivityAction.onDidChangeActivity(() => { this.compositeActivity = null; this.updateActivity(); }, this, this._callOnDispose); } protected get activity(): IActivity { From 3f25a1b7cb5b50cc1c2fcbb59431c76e4cbeafe8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 14:44:31 +0200 Subject: [PATCH 046/149] fix #50187 --- src/vs/editor/contrib/suggest/suggestController.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 1d1cf7b049ccc..e073751d3f3d4 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -196,10 +196,12 @@ export class SuggestController implements IEditorContribution { const editorColumn = this._editor.getPosition().column; const columnDelta = editorColumn - position.column; + // pushing undo stops *before* additional text edits and + // *after* the main edit + this._editor.pushUndoStop(); + if (Array.isArray(suggestion.additionalTextEdits)) { - this._editor.pushUndoStop(); this._editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); - this._editor.pushUndoStop(); } // keep item in memory @@ -213,9 +215,12 @@ export class SuggestController implements IEditorContribution { SnippetController2.get(this._editor).insert( insertText, suggestion.overwriteBefore + columnDelta, - suggestion.overwriteAfter + suggestion.overwriteAfter, + false, false ); + this._editor.pushUndoStop(); + if (!suggestion.command) { // done this._model.cancel(); From 22a98f386bd95562ca24f9cb613be613721b98db Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Jun 2018 15:00:18 +0200 Subject: [PATCH 047/149] fileSystemProvider: isReadonly --- src/vs/platform/files/common/files.ts | 8 +++++++- src/vs/vscode.d.ts | 2 +- src/vs/workbench/api/node/extHostFileSystem.ts | 5 ++++- .../services/files/electron-browser/fileService.ts | 2 ++ .../services/files/electron-browser/remoteFileService.ts | 1 + 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 24188ef8c4df2..9ab1c0be2955b 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -182,7 +182,8 @@ export enum FileSystemProviderCapabilities { FileOpenReadWriteClose = 1 << 2, FileFolderCopy = 1 << 3, - PathCaseSensitive = 1 << 10 + PathCaseSensitive = 1 << 10, + Readonly = 1 << 11 } export interface IFileSystemProvider { @@ -411,6 +412,11 @@ export interface IFileStat extends IBaseStat { */ isDirectory: boolean; + /** + * The resource is readonly. + */ + isReadonly?: boolean; + /** * The resource is a symbolic link. */ diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 60f7435509c80..026f84b57b104 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -6863,7 +6863,7 @@ declare module 'vscode' { * @param options Immutable metadata about the provider. * @return A [disposable](#Disposable) that unregisters this provider when being disposed. */ - export function registerFileSystemProvider(scheme: string, provider: FileSystemProvider, options?: { isCaseSensitive?: boolean }): Disposable; + export function registerFileSystemProvider(scheme: string, provider: FileSystemProvider, options?: { isCaseSensitive?: boolean, isReadonly?: boolean }): Disposable; } /** diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 2fa4cc8844fe1..58f06eb2188b5 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -83,7 +83,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { extHostLanguageFeatures.registerDocumentLinkProvider('*', this._linkProvider); } - registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean } = {}) { + registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) { if (this._usedSchemes.has(scheme)) { throw new Error(`a provider for the scheme '${scheme}' is already registered`); @@ -98,6 +98,9 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { if (options.isCaseSensitive) { capabilites += files.FileSystemProviderCapabilities.PathCaseSensitive; } + if (options.isReadonly) { + capabilites += files.FileSystemProviderCapabilities.Readonly; + } if (typeof provider.copy === 'function') { capabilites += files.FileSystemProviderCapabilities.FileFolderCopy; } diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index d8501eb66b0ae..e2021d010ce14 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -1129,6 +1129,7 @@ export class StatResolver { resource: this.resource, isDirectory: this.isDirectory, isSymbolicLink: this.isSymbolicLink, + isReadonly: false, name: this.name, etag: this.etag, size: this.size, @@ -1213,6 +1214,7 @@ export class StatResolver { resource: fileResource, isDirectory: fileStat.isDirectory(), isSymbolicLink, + isReadonly: false, name: file, mtime: fileStat.mtime.getTime(), etag: etag(fileStat), diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 0dca336c1a788..d03b24db8bd82 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -45,6 +45,7 @@ function toIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], recurse name: posix.basename(resource.path), isDirectory: (stat.type & FileType.Directory) !== 0, isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0, + isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly), mtime: stat.mtime, size: stat.size, etag: stat.mtime.toString(29) + stat.size.toString(31), From 10192366ebd5dcc6fbd006ecc9c4c0cedd6c4efe Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 15:17:54 +0200 Subject: [PATCH 048/149] move onDidRename to filesystem-events things, #43768 --- .../src/features/updatePathsOnRename.ts | 4 +- src/vs/vscode.proposed.d.ts | 8 ++-- .../electron-browser/mainThreadDocuments.ts | 8 +--- .../mainThreadFileSystemEventService.ts | 22 ++++++++--- src/vs/workbench/api/node/extHost.api.impl.ts | 4 +- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- src/vs/workbench/api/node/extHostDocuments.ts | 9 ----- .../api/node/extHostFileSystemEventService.ts | 39 +++++++++++-------- 8 files changed, 49 insertions(+), 47 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index c8d16ab255903..d1b9be9fd3bcd 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -34,8 +34,8 @@ export class UpdateImportsOnFileRenameHandler { private readonly fileConfigurationManager: FileConfigurationManager, private readonly _handles: (uri: vscode.Uri) => Promise, ) { - this._onDidRenameSub = vscode.workspace.onDidRenameResource(e => { - this.doRename(e.oldResource, e.newResource); + this._onDidRenameSub = vscode.workspace.onDidRenameFile(e => { + this.doRename(e.oldUri, e.newUri); }); } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 69c74a17ebb59..6e4e551b5d956 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -629,13 +629,13 @@ declare module 'vscode' { //#endregion //#region mjbvz: File rename events - export interface ResourceRenamedEvent { - readonly oldResource: Uri; - readonly newResource: Uri; + export interface FileRenameEvent { + readonly oldUri: Uri; + readonly newUri: Uri; } export namespace workspace { - export const onDidRenameResource: Event; + export const onDidRenameFile: Event; } //#endregion diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts index a85c61c9d4e26..4474c00ee09f3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocuments.ts @@ -10,7 +10,7 @@ import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; import { TextFileModelChangeEvent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileService, FileOperation } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ExtHostContext, MainThreadDocumentsShape, ExtHostDocumentsShape, IExtHostContext } from '../node/extHost.protocol'; @@ -119,12 +119,6 @@ export class MainThreadDocuments implements MainThreadDocumentsShape { } })); - this._toDispose.push(fileService.onAfterOperation(e => { - if (e.operation === FileOperation.MOVE) { - this._proxy.$onDidRename(e.resource, e.target.resource); - } - })); - this._modelToDisposeMap = Object.create(null); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts index cda620bf166dc..c872d04ac1dd3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts @@ -4,15 +4,16 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { FileChangeType, IFileService } from 'vs/platform/files/common/files'; -import { ExtHostContext, ExtHostFileSystemEventServiceShape, FileSystemEvents, IExtHostContext } from '../node/extHost.protocol'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { FileChangeType, IFileService, FileOperation } from 'vs/platform/files/common/files'; import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { ExtHostContext, ExtHostFileSystemEventServiceShape, FileSystemEvents, IExtHostContext } from '../node/extHost.protocol'; @extHostCustomer export class MainThreadFileSystemEventService { - private readonly _listener: IDisposable; + private readonly _fileEventListener: IDisposable; + private readonly _fileOperationListener: IDisposable; constructor( extHostContext: IExtHostContext, @@ -20,13 +21,14 @@ export class MainThreadFileSystemEventService { ) { const proxy: ExtHostFileSystemEventServiceShape = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService); + + // file system events - (changes the editor and other make) const events: FileSystemEvents = { created: [], changed: [], deleted: [] }; - - this._listener = fileService.onFileChanges(event => { + this._fileEventListener = fileService.onFileChanges(event => { for (let change of event.changes) { switch (change.type) { case FileChangeType.ADDED: @@ -46,9 +48,17 @@ export class MainThreadFileSystemEventService { events.changed.length = 0; events.deleted.length = 0; }); + + // file operation events - (changes the editor makes) + this._fileOperationListener = fileService.onAfterOperation(e => { + if (e.operation === FileOperation.MOVE) { + proxy.$onFileRename(e.resource, e.target.resource); + } + }); } dispose(): void { - this._listener.dispose(); + this._fileEventListener.dispose(); + this._fileOperationListener.dispose(); } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 5bf7319a8325f..59476e8887290 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -576,8 +576,8 @@ export function createApiFactory( registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => { return exthostCommentProviders.registerWorkspaceCommentProvider(provider); }), - onDidRenameResource: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { - return extHostDocuments.onDidRenameResource(listener, thisArg, disposables); + onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { + return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); }) }; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index c43d5a27b6eff..3c4fce858fd9a 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -614,7 +614,6 @@ export interface ExtHostDocumentsShape { $acceptModelSaved(strURL: UriComponents): void; $acceptDirtyStateChanged(strURL: UriComponents, isDirty: boolean): void; $acceptModelChanged(strURL: UriComponents, e: IModelChangedEvent, isDirty: boolean): void; - $onDidRename(oldURL: UriComponents, newURL: UriComponents): void; } export interface ExtHostDocumentSaveParticipantShape { @@ -698,6 +697,7 @@ export interface FileSystemEvents { } export interface ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents): void; + $onFileRename(oldUri: UriComponents, newUri: UriComponents): void; } export interface ObjectIdentifier { diff --git a/src/vs/workbench/api/node/extHostDocuments.ts b/src/vs/workbench/api/node/extHostDocuments.ts index e9122c83d72e8..7cc0f42359cc7 100644 --- a/src/vs/workbench/api/node/extHostDocuments.ts +++ b/src/vs/workbench/api/node/extHostDocuments.ts @@ -21,13 +21,11 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { private _onDidRemoveDocument = new Emitter(); private _onDidChangeDocument = new Emitter(); private _onDidSaveDocument = new Emitter(); - private _onDidRenameResource = new Emitter(); readonly onDidAddDocument: Event = this._onDidAddDocument.event; readonly onDidRemoveDocument: Event = this._onDidRemoveDocument.event; readonly onDidChangeDocument: Event = this._onDidChangeDocument.event; readonly onDidSaveDocument: Event = this._onDidSaveDocument.event; - readonly onDidRenameResource: Event = this._onDidRenameResource.event; private _toDispose: IDisposable[]; private _proxy: MainThreadDocumentsShape; @@ -150,11 +148,4 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { public setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void { setWordDefinitionFor(modeId, wordDefinition); } - - public $onDidRename(oldURL: UriComponents, newURL: UriComponents): void { - const oldResource = URI.revive(oldURL); - const newResource = URI.revive(newURL); - this._onDidRenameResource.fire({ oldResource, newResource }); - } - } diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index eeb7b256dcc77..22a3963b9a220 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -4,18 +4,18 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IRelativePattern, parse } from 'vs/base/common/glob'; +import URI, { UriComponents } from 'vs/base/common/uri'; +import * as vscode from 'vscode'; +import { ExtHostFileSystemEventServiceShape, FileSystemEvents } from './extHost.protocol'; import { Disposable } from './extHostTypes'; -import { parse, IRelativePattern } from 'vs/base/common/glob'; -import { Uri, FileSystemWatcher as _FileSystemWatcher } from 'vscode'; -import { FileSystemEvents, ExtHostFileSystemEventServiceShape } from './extHost.protocol'; -import URI from 'vs/base/common/uri'; -class FileSystemWatcher implements _FileSystemWatcher { +class FileSystemWatcher implements vscode.FileSystemWatcher { - private _onDidCreate = new Emitter(); - private _onDidChange = new Emitter(); - private _onDidDelete = new Emitter(); + private _onDidCreate = new Emitter(); + private _onDidChange = new Emitter(); + private _onDidDelete = new Emitter(); private _disposable: Disposable; private _config: number; @@ -80,31 +80,38 @@ class FileSystemWatcher implements _FileSystemWatcher { this._disposable.dispose(); } - get onDidCreate(): Event { + get onDidCreate(): Event { return this._onDidCreate.event; } - get onDidChange(): Event { + get onDidChange(): Event { return this._onDidChange.event; } - get onDidDelete(): Event { + get onDidDelete(): Event { return this._onDidDelete.event; } } export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape { - private _emitter = new Emitter(); + private _onFileEvent = new Emitter(); + private _onDidRenameFile = new Emitter(); + + readonly onDidRenameFile: Event = this._onDidRenameFile.event; constructor() { } - public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): _FileSystemWatcher { - return new FileSystemWatcher(this._emitter.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents); + public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher { + return new FileSystemWatcher(this._onFileEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents); } $onFileEvent(events: FileSystemEvents) { - this._emitter.fire(events); + this._onFileEvent.fire(events); + } + + $onFileRename(oldUri: UriComponents, newUri: UriComponents) { + this._onDidRenameFile.fire(Object.freeze({ oldUri: URI.revive(oldUri), newUri: URI.revive(newUri) })); } } From 48087bf93760144a8a40a1112862379a070de398 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Jun 2018 15:47:27 +0200 Subject: [PATCH 049/149] explorerResourceIsReadonly context --- .../workbench/parts/files/common/explorerModel.ts | 14 ++++++++++---- src/vs/workbench/parts/files/common/files.ts | 2 ++ .../files/electron-browser/views/explorerView.ts | 5 ++++- .../test/electron-browser/explorerModel.test.ts | 10 +++++----- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index ce016239d03b1..3fd7f8d95460b 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -25,7 +25,7 @@ export class Model { constructor(@IWorkspaceContextService private contextService: IWorkspaceContextService) { const setRoots = () => this._roots = this.contextService.getWorkspace().folders - .map(folder => new ExplorerItem(folder.uri, undefined, false, true, folder.name)); + .map(folder => new ExplorerItem(folder.uri, undefined, false, false, true, folder.name)); this._listener = this.contextService.onDidChangeWorkspaceFolders(() => setRoots()); setRoots(); } @@ -72,16 +72,18 @@ export class ExplorerItem { public etag: string; private _isDirectory: boolean; private _isSymbolicLink: boolean; + private _isReadonly: boolean; private children: Map; public parent: ExplorerItem; public isDirectoryResolved: boolean; - constructor(resource: URI, public root: ExplorerItem, isSymbolicLink?: boolean, isDirectory?: boolean, name: string = resources.basenameOrAuthority(resource), mtime?: number, etag?: string) { + constructor(resource: URI, public root: ExplorerItem, isSymbolicLink?: boolean, isReadonly?: boolean, isDirectory?: boolean, name: string = resources.basenameOrAuthority(resource), mtime?: number, etag?: string) { this.resource = resource; this._name = name; this.isDirectory = !!isDirectory; this._isSymbolicLink = !!isSymbolicLink; + this._isReadonly = !!isReadonly; this.etag = etag; this.mtime = mtime; @@ -100,6 +102,10 @@ export class ExplorerItem { return this._isDirectory; } + public get isReadonly(): boolean { + return this._isReadonly; + } + public set isDirectory(value: boolean) { if (value !== this._isDirectory) { this._isDirectory = value; @@ -140,7 +146,7 @@ export class ExplorerItem { } public static create(raw: IFileStat, root: ExplorerItem, resolveTo?: URI[]): ExplorerItem { - const stat = new ExplorerItem(raw.resource, root, raw.isSymbolicLink, raw.isDirectory, raw.name, raw.mtime, raw.etag); + const stat = new ExplorerItem(raw.resource, root, raw.isSymbolicLink, raw.isReadonly, raw.isDirectory, raw.name, raw.mtime, raw.etag); // Recursively add children if present if (stat.isDirectory) { @@ -395,7 +401,7 @@ export class NewStatPlaceholder extends ExplorerItem { private directoryPlaceholder: boolean; constructor(isDirectory: boolean, root: ExplorerItem) { - super(URI.file(''), root, false, false, NewStatPlaceholder.NAME); + super(URI.file(''), root, false, false, false, NewStatPlaceholder.NAME); this.id = NewStatPlaceholder.ID++; this.isDirectoryResolved = isDirectory; diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 26c9bd956ca00..da4464d38d338 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -50,10 +50,12 @@ const openEditorsVisibleId = 'openEditorsVisible'; const openEditorsFocusId = 'openEditorsFocus'; const explorerViewletFocusId = 'explorerViewletFocus'; const explorerResourceIsFolderId = 'explorerResourceIsFolder'; +const explorerResourceIsReadonly = 'explorerResourceIsReadonly'; const explorerResourceIsRootId = 'explorerResourceIsRoot'; export const ExplorerViewletVisibleContext = new RawContextKey(explorerViewletVisibleId, true); export const ExplorerFolderContext = new RawContextKey(explorerResourceIsFolderId, false); +export const ExplorerResourceIsReadonlyContext = new RawContextKey(explorerResourceIsReadonly, false); export const ExplorerRootContext = new RawContextKey(explorerResourceIsRootId, false); export const FilesExplorerFocusedContext = new RawContextKey(filesExplorerFocusId, true); export const OpenEditorsVisibleContext = new RawContextKey(openEditorsVisibleId, false); diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index d9507f86bece4..a63f0444c861f 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -14,7 +14,7 @@ import * as resources from 'vs/base/common/resources'; import * as glob from 'vs/base/common/glob'; import { Action, IAction } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; -import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, SortOrderConfiguration, SortOrder, IExplorerView, ExplorerRootContext } from 'vs/workbench/parts/files/common/files'; +import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, SortOrderConfiguration, SortOrder, IExplorerView, ExplorerRootContext, ExplorerResourceIsReadonlyContext } from 'vs/workbench/parts/files/common/files'; import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileService, FILES_EXCLUDE_CONFIG } from 'vs/platform/files/common/files'; import { RefreshViewExplorerAction, NewFolderAction, NewFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { FileDragAndDrop, FileFilter, FileSorter, FileController, FileRenderer, FileDataSource, FileViewletState, FileAccessibilityProvider } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; @@ -67,6 +67,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView private resourceContext: ResourceContextKey; private folderContext: IContextKey; + private readonlyContext: IContextKey; private rootContext: IContextKey; private fileEventsFilter: ResourceGlobMatcher; @@ -104,6 +105,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView this.resourceContext = instantiationService.createInstance(ResourceContextKey); this.folderContext = ExplorerFolderContext.bindTo(contextKeyService); + this.readonlyContext = ExplorerResourceIsReadonlyContext.bindTo(contextKeyService); this.rootContext = ExplorerRootContext.bindTo(contextKeyService); this.fileEventsFilter = instantiationService.createInstance( @@ -433,6 +435,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView const resource = e.focus ? e.focus.resource : isSingleFolder ? this.contextService.getWorkspace().folders[0].uri : undefined; this.resourceContext.set(resource); this.folderContext.set((isSingleFolder && !e.focus) || e.focus && e.focus.isDirectory); + this.readonlyContext.set(e.focus && e.focus.isReadonly); this.rootContext.set(!e.focus || (e.focus && e.focus.isRoot)); })); diff --git a/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts b/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts index 515ad42c58021..210ff1bdb780e 100644 --- a/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts +++ b/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts @@ -14,7 +14,7 @@ import { validateFileName } from 'vs/workbench/parts/files/electron-browser/file import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; function createStat(path: string, name: string, isFolder: boolean, hasChildren: boolean, size: number, mtime: number): ExplorerItem { - return new ExplorerItem(toResource(path), undefined, false, isFolder, name, mtime); + return new ExplorerItem(toResource(path), undefined, false, false, isFolder, name, mtime); } function toResource(path) { @@ -264,20 +264,20 @@ suite('Files - View Model', () => { test('Merge Local with Disk', function () { const d = new Date().toUTCString(); - const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, false, true, 'to', Date.now(), d); - const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, false, true, 'to', Date.now(), new Date(0).toUTCString()); + const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, false, false, true, 'to', Date.now(), d); + const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, false, false, true, 'to', Date.now(), new Date(0).toUTCString()); // Merge Properties ExplorerItem.mergeLocalWithDisk(merge2, merge1); assert.strictEqual(merge1.mtime, merge2.mtime); // Merge Child when isDirectoryResolved=false is a no-op - merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, false, true, 'foo.html', Date.now(), d)); + merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, false, false, true, 'foo.html', Date.now(), d)); ExplorerItem.mergeLocalWithDisk(merge2, merge1); assert.strictEqual(merge1.getChildrenArray().length, 0); // Merge Child with isDirectoryResolved=true - const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, false, true, 'foo.html', Date.now(), d); + const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, false, false, true, 'foo.html', Date.now(), d); merge2.removeChild(child); merge2.addChild(child); merge2.isDirectoryResolved = true; From 2579a137248ade51447f69d75cdf2306f6061e6a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 15:54:33 +0200 Subject: [PATCH 050/149] add (internal) api for onWillSave #43768 --- .../mainThreadFileSystemEventService.ts | 29 +++-- src/vs/workbench/api/node/extHost.protocol.ts | 1 + .../api/node/extHostFileSystemEventService.ts | 5 + .../textfile/common/textFileService.ts | 106 ++++++++++-------- .../services/textfile/common/textfiles.ts | 9 ++ 5 files changed, 94 insertions(+), 56 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts index c872d04ac1dd3..5b6e9cf18be4e 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts @@ -4,23 +4,24 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { FileChangeType, IFileService, FileOperation } from 'vs/platform/files/common/files'; import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { ExtHostContext, ExtHostFileSystemEventServiceShape, FileSystemEvents, IExtHostContext } from '../node/extHost.protocol'; +import { ExtHostContext, FileSystemEvents, IExtHostContext } from '../node/extHost.protocol'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @extHostCustomer export class MainThreadFileSystemEventService { - private readonly _fileEventListener: IDisposable; - private readonly _fileOperationListener: IDisposable; + private readonly _listener = new Array(); constructor( extHostContext: IExtHostContext, - @IFileService fileService: IFileService + @IFileService fileService: IFileService, + @ITextFileService textfileService: ITextFileService, ) { - const proxy: ExtHostFileSystemEventServiceShape = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService); + const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService); // file system events - (changes the editor and other make) const events: FileSystemEvents = { @@ -28,7 +29,7 @@ export class MainThreadFileSystemEventService { changed: [], deleted: [] }; - this._fileEventListener = fileService.onFileChanges(event => { + fileService.onFileChanges(event => { for (let change of event.changes) { switch (change.type) { case FileChangeType.ADDED: @@ -47,18 +48,22 @@ export class MainThreadFileSystemEventService { events.created.length = 0; events.changed.length = 0; events.deleted.length = 0; - }); + }, undefined, this._listener); // file operation events - (changes the editor makes) - this._fileOperationListener = fileService.onAfterOperation(e => { + fileService.onAfterOperation(e => { if (e.operation === FileOperation.MOVE) { proxy.$onFileRename(e.resource, e.target.resource); } - }); + }, undefined, this._listener); + + textfileService.onWillMove(e => { + let promise = proxy.$onWillRename(e.oldResource, e.newResource); + e.waitUntil(promise); + }, undefined, this._listener); } dispose(): void { - this._fileEventListener.dispose(); - this._fileOperationListener.dispose(); + dispose(this._listener); } } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 3c4fce858fd9a..5dd9e5517bc8e 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -698,6 +698,7 @@ export interface FileSystemEvents { export interface ExtHostFileSystemEventServiceShape { $onFileEvent(events: FileSystemEvents): void; $onFileRename(oldUri: UriComponents, newUri: UriComponents): void; + $onWillRename(oldUri: UriComponents, newUri: UriComponents): TPromise; } export interface ObjectIdentifier { diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index 22a3963b9a220..5fd040d24a6b6 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -10,6 +10,7 @@ import URI, { UriComponents } from 'vs/base/common/uri'; import * as vscode from 'vscode'; import { ExtHostFileSystemEventServiceShape, FileSystemEvents } from './extHost.protocol'; import { Disposable } from './extHostTypes'; +import { TPromise } from 'vs/base/common/winjs.base'; class FileSystemWatcher implements vscode.FileSystemWatcher { @@ -114,4 +115,8 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ $onFileRename(oldUri: UriComponents, newUri: UriComponents) { this._onDidRenameFile.fire(Object.freeze({ oldUri: URI.revive(oldUri), newUri: URI.revive(newUri) })); } + + $onWillRename(oldUri: UriComponents, newUri: UriComponents): TPromise { + return undefined; + } } diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 3e1b6a3f3a36e..0e4d700b9d0e5 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -14,7 +14,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IResult, ITextFileOperationResult, ITextFileService, IRawTextContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext } from 'vs/workbench/services/textfile/common/textfiles'; +import { IResult, ITextFileOperationResult, ITextFileService, IRawTextContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext, IWillMoveEvent } from 'vs/workbench/services/textfile/common/textfiles'; import { ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -48,6 +48,9 @@ export abstract class TextFileService implements ITextFileService { public _serviceBrand: any; + private readonly _onWillMove = new Emitter(); + readonly onWillMove: Event = this._onWillMove.event; + private toUnbind: IDisposable[]; private _models: TextFileEditorModelManager; @@ -710,60 +713,75 @@ export abstract class TextFileService implements ITextFileService { public move(source: URI, target: URI, overwrite?: boolean): TPromise { - // Handle target models if existing (if target URI is a folder, this can be multiple) - let handleTargetModelPromise: TPromise = TPromise.as(void 0); - const dirtyTargetModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), target, !platform.isLinux /* ignorecase */)); - if (dirtyTargetModels.length) { - handleTargetModelPromise = this.revertAll(dirtyTargetModels.map(targetModel => targetModel.getResource()), { soft: true }); - } + const waitForPromises: TPromise[] = []; + this._onWillMove.fire({ + oldResource: source, + newResource: target, + waitUntil(p: TPromise) { + waitForPromises.push(TPromise.wrap(p).then(undefined, errors.onUnexpectedError)); + } + }); - return handleTargetModelPromise.then(() => { + // prevent async waitUntil-calls + Object.freeze(waitForPromises); - // Handle dirty source models if existing (if source URI is a folder, this can be multiple) - let handleDirtySourceModels: TPromise; - const dirtySourceModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), source, !platform.isLinux /* ignorecase */)); - const dirtyTargetModels: URI[] = []; - if (dirtySourceModels.length) { - handleDirtySourceModels = TPromise.join(dirtySourceModels.map(sourceModel => { - const sourceModelResource = sourceModel.getResource(); - let targetModelResource: URI; + return TPromise.join(waitForPromises).then(() => { - // If the source is the actual model, just use target as new resource - if (isEqual(sourceModelResource, source, !platform.isLinux /* ignorecase */)) { - targetModelResource = target; - } + // Handle target models if existing (if target URI is a folder, this can be multiple) + let handleTargetModelPromise: TPromise = TPromise.as(void 0); + const dirtyTargetModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), target, !platform.isLinux /* ignorecase */)); + if (dirtyTargetModels.length) { + handleTargetModelPromise = this.revertAll(dirtyTargetModels.map(targetModel => targetModel.getResource()), { soft: true }); + } - // Otherwise a parent folder of the source is being moved, so we need - // to compute the target resource based on that - else { - targetModelResource = sourceModelResource.with({ path: paths.join(target.path, sourceModelResource.path.substr(source.path.length + 1)) }); - } + return handleTargetModelPromise.then(() => { - // Remember as dirty target model to load after the operation - dirtyTargetModels.push(targetModelResource); + // Handle dirty source models if existing (if source URI is a folder, this can be multiple) + let handleDirtySourceModels: TPromise; + const dirtySourceModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), source, !platform.isLinux /* ignorecase */)); + const dirtyTargetModels: URI[] = []; + if (dirtySourceModels.length) { + handleDirtySourceModels = TPromise.join(dirtySourceModels.map(sourceModel => { + const sourceModelResource = sourceModel.getResource(); + let targetModelResource: URI; - // Backup dirty source model to the target resource it will become later - return this.backupFileService.backupResource(targetModelResource, sourceModel.createSnapshot(), sourceModel.getVersionId()); - })); - } else { - handleDirtySourceModels = TPromise.as(void 0); - } + // If the source is the actual model, just use target as new resource + if (isEqual(sourceModelResource, source, !platform.isLinux /* ignorecase */)) { + targetModelResource = target; + } + + // Otherwise a parent folder of the source is being moved, so we need + // to compute the target resource based on that + else { + targetModelResource = sourceModelResource.with({ path: paths.join(target.path, sourceModelResource.path.substr(source.path.length + 1)) }); + } - return handleDirtySourceModels.then(() => { + // Remember as dirty target model to load after the operation + dirtyTargetModels.push(targetModelResource); - // Soft revert the dirty source files if any - return this.revertAll(dirtySourceModels.map(dirtySourceModel => dirtySourceModel.getResource()), { soft: true }).then(() => { + // Backup dirty source model to the target resource it will become later + return this.backupFileService.backupResource(targetModelResource, sourceModel.createSnapshot(), sourceModel.getVersionId()); + })); + } else { + handleDirtySourceModels = TPromise.as(void 0); + } + + return handleDirtySourceModels.then(() => { - // Rename to target - return this.fileService.moveFile(source, target, overwrite).then(() => { + // Soft revert the dirty source files if any + return this.revertAll(dirtySourceModels.map(dirtySourceModel => dirtySourceModel.getResource()), { soft: true }).then(() => { - // Load models that were dirty before - return TPromise.join(dirtyTargetModels.map(dirtyTargetModel => this.models.loadOrCreate(dirtyTargetModel))).then(() => void 0); - }, error => { + // Rename to target + return this.fileService.moveFile(source, target, overwrite).then(() => { - // In case of an error, discard any dirty target backups that were made - return TPromise.join(dirtyTargetModels.map(dirtyTargetModel => this.backupFileService.discardResourceBackup(dirtyTargetModel))) - .then(() => TPromise.wrapError(error)); + // Load models that were dirty before + return TPromise.join(dirtyTargetModels.map(dirtyTargetModel => this.models.loadOrCreate(dirtyTargetModel))).then(() => void 0); + }, error => { + + // In case of an error, discard any dirty target backups that were made + return TPromise.join(dirtyTargetModels.map(dirtyTargetModel => this.backupFileService.discardResourceBackup(dirtyTargetModel))) + .then(() => TPromise.wrapError(error)); + }); }); }); }); diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index c5c6f3164dd82..a59d72866223c 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -235,12 +235,21 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport isDisposed(): boolean; } + +export interface IWillMoveEvent { + oldResource: URI; + newResource: URI; + waitUntil(p: TPromise): void; +} + export interface ITextFileService extends IDisposable { _serviceBrand: any; readonly onAutoSaveConfigurationChange: Event; readonly onFilesAssociationChange: Event; + onWillMove: Event; + readonly isHotExitEnabled: boolean; /** From 6dcfa8fbfed9c1ff80bd0024e71f766f1f5f6070 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 16:13:14 +0200 Subject: [PATCH 051/149] add proposed onWillRenameFile event, #43768 --- src/vs/vscode.proposed.d.ts | 7 +++++++ src/vs/workbench/api/node/extHost.api.impl.ts | 3 +++ .../api/node/extHostFileSystemEventService.ts | 20 ++++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6e4e551b5d956..13c728dda87be 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -634,7 +634,14 @@ declare module 'vscode' { readonly newUri: Uri; } + export interface FileWillRenameEvent { + readonly oldUri: Uri; + readonly newUri: Uri; + waitUntil(thenable: Thenable): void; + } + export namespace workspace { + export const onWillRenameFile: Event; export const onDidRenameFile: Event; } //#endregion diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 59476e8887290..8002c97083002 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -578,6 +578,9 @@ export function createApiFactory( }), onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); + }), + onWillRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { + return extHostFileSystemEvent.onWillRenameFile(listener, thisArg, disposables); }) }; diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index 5fd040d24a6b6..476a96a3c0013 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -98,8 +98,10 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ private _onFileEvent = new Emitter(); private _onDidRenameFile = new Emitter(); + private _onWillRenameFile = new Emitter(); readonly onDidRenameFile: Event = this._onDidRenameFile.event; + readonly onWillRenameFile: Event = this._onWillRenameFile.event; constructor() { } @@ -117,6 +119,22 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ } $onWillRename(oldUri: UriComponents, newUri: UriComponents): TPromise { - return undefined; + + let thenables: Thenable[] = []; + + this._onWillRenameFile.fire({ + oldUri: URI.revive(oldUri), + newUri: URI.revive(newUri), + waitUntil(thenable: Thenable): void { + if (Object.isFrozen(thenables)) { + throw new Error('waitUntil cannot be called async'); + } + thenables.push(thenable.then(undefined, err => console.error(err))); + } + }); + + Object.freeze(thenables); + + return TPromise.join(thenables); } } From 6b8ea727a918c6d646e8ea368cd7e75da5a03c48 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 16:15:34 +0200 Subject: [PATCH 052/149] update doc comment --- src/vs/vscode.proposed.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 13c728dda87be..edfb02388f4b1 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -628,7 +628,7 @@ declare module 'vscode' { //#endregion - //#region mjbvz: File rename events + //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 export interface FileRenameEvent { readonly oldUri: Uri; readonly newUri: Uri; From 1ae8202e4e396ff2e5e4438bb5238a1bce3bc26d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Jun 2018 16:16:52 +0200 Subject: [PATCH 053/149] Use posix.join --- .../platform/extensionManagement/node/extensionLifecycle.ts | 4 ++-- src/vs/platform/localizations/node/localizations.ts | 6 +++--- .../parts/extensions/node/extensionsWorkbenchService.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 170ec07d73243..7589d4398765b 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ILogService } from 'vs/platform/log/common/log'; import { fork, ChildProcess } from 'child_process'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { join } from 'vs/base/common/paths'; +import { posix } from 'path'; import { Limiter } from 'vs/base/common/async'; import { fromNodeEventEmitter, anyEvent, mapEvent, debounceEvent } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; @@ -43,7 +43,7 @@ export class ExtensionsLifecycle extends Disposable { this.logService.warn(extension.identifier.id, 'Uninstall script should be a node script'); return null; } - return { uninstallHook: join(extension.location.fsPath, uninstallScript[1]), args: uninstallScript.slice(2) || [] }; + return { uninstallHook: posix.join(extension.location.fsPath, uninstallScript[1]), args: uninstallScript.slice(2) || [] }; } return null; } diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts index 6a5a49b1f6220..2e7b180fc83f3 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/localizations/node/localizations.ts @@ -8,7 +8,6 @@ import { createHash } from 'crypto'; import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { Disposable } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { join } from 'vs/base/common/paths'; import { TPromise } from 'vs/base/common/winjs.base'; import { Limiter } from 'vs/base/common/async'; import { areSameExtensions, getGalleryExtensionIdFromLocal, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -18,6 +17,7 @@ import product from 'vs/platform/node/product'; import { distinct, equals } from 'vs/base/common/arrays'; import { Event, Emitter } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; +import { posix } from 'path'; interface ILanguagePack { hash: string; @@ -107,7 +107,7 @@ class LanguagePacksCache extends Disposable { @ILogService private logService: ILogService ) { super(); - this.languagePacksFilePath = join(environmentService.userDataPath, 'languagepacks.json'); + this.languagePacksFilePath = posix.join(environmentService.userDataPath, 'languagepacks.json'); this.languagePacksFileLimiter = new Limiter(1); } @@ -152,7 +152,7 @@ class LanguagePacksCache extends Disposable { languagePack.extensions.push({ extensionIdentifier, version: extension.manifest.version }); } for (const translation of localizationContribution.translations) { - languagePack.translations[translation.id] = join(extension.location.path, translation.path); + languagePack.translations[translation.id] = posix.join(extension.location.fsPath, translation.path); } } } diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 1259babb2dd28..c5aaaea043000 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -39,7 +39,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { groupBy } from 'vs/base/common/collections'; import { Schemas } from 'vs/base/common/network'; -import { join } from 'vs/base/common/paths'; +import { posix } from 'path'; interface IExtensionStateProvider { (extension: Extension): T; @@ -138,7 +138,7 @@ class Extension implements IExtension { private get localIconUrl(): string { if (this.local && this.local.manifest.icon) { - return this.local.location.with({ path: join(this.local.location.path, this.local.manifest.icon) }).toString(); + return this.local.location.with({ path: posix.join(this.local.location.path, this.local.manifest.icon) }).toString(); } return null; } From 45bfc0a21514bc799804854695d97e0267384257 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 16:28:19 +0200 Subject: [PATCH 054/149] enable more tests #10659 --- .../electron-browser/api/extHostTypes.test.ts | 83 ++++++++----------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index 9c868b31e5e16..6a9037d6d82e2 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -373,55 +373,44 @@ suite('ExtHostTypes', function () { edit.set(b, [types.TextEdit.insert(new types.Position(0, 0), 'ffff')]); assert.equal(edit.get(b).length, 1); - }); - // test('WorkspaceEdit should fail when editing deleted resource', () => { - // const resource = URI.parse('file:///a.ts'); - - // const edit = new types.WorkspaceEdit(); - // edit.deleteResource(resource); - // try { - // edit.insert(resource, new types.Position(0, 0), ''); - // assert.fail(false, 'Should disallow edit of deleted resource'); - // } catch { - // // expected - // } - // }); - - // test('WorkspaceEdit - keep order of text and file changes', function () { - - // const edit = new types.WorkspaceEdit(); - // edit.replace(URI.parse('foo:a'), new types.Range(1, 1, 1, 1), 'foo'); - // edit.renameResource(URI.parse('foo:a'), URI.parse('foo:b')); - // edit.replace(URI.parse('foo:a'), new types.Range(2, 1, 2, 1), 'bar'); - // edit.replace(URI.parse('foo:b'), new types.Range(3, 1, 3, 1), 'bazz'); - - // const all = edit.allEntries(); - // assert.equal(all.length, 3); - - // function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI]): thing is [URI, URI] { - // const [f, s] = thing; - // return URI.isUri(f) && URI.isUri(s); - // } - - // function isTextChange(thing: [URI, types.TextEdit[]] | [URI, URI]): thing is [URI, types.TextEdit[]] { - // const [f, s] = thing; - // return URI.isUri(f) && Array.isArray(s); - // } - - // const [first, second, third] = all; - // assert.equal(first[0].toString(), 'foo:a'); - // assert.ok(!isFileChange(first)); - // assert.ok(isTextChange(first) && first[1].length === 2); - - // assert.equal(second[0].toString(), 'foo:a'); - // assert.ok(isFileChange(second)); - - // assert.equal(third[0].toString(), 'foo:b'); - // assert.ok(!isFileChange(third)); - // assert.ok(isTextChange(third) && third[1].length === 1); - // }); + test('WorkspaceEdit - keep order of text and file changes', function () { + + const edit = new types.WorkspaceEdit(); + edit.replace(URI.parse('foo:a'), new types.Range(1, 1, 1, 1), 'foo'); + edit.renameFile(URI.parse('foo:a'), URI.parse('foo:b')); + edit.replace(URI.parse('foo:a'), new types.Range(2, 1, 2, 1), 'bar'); + edit.replace(URI.parse('foo:b'), new types.Range(3, 1, 3, 1), 'bazz'); + + const all = edit.allEntries(); + assert.equal(all.length, 4); + + function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI]): thing is [URI, URI] { + const [f, s] = thing; + return URI.isUri(f) && URI.isUri(s); + } + + function isTextChange(thing: [URI, types.TextEdit[]] | [URI, URI]): thing is [URI, types.TextEdit[]] { + const [f, s] = thing; + return URI.isUri(f) && Array.isArray(s); + } + + const [first, second, third, fourth] = all; + assert.equal(first[0].toString(), 'foo:a'); + assert.ok(!isFileChange(first)); + assert.ok(isTextChange(first) && first[1].length === 1); + + assert.equal(second[0].toString(), 'foo:a'); + assert.ok(isFileChange(second)); + + assert.equal(third[0].toString(), 'foo:a'); + assert.ok(isTextChange(third) && third[1].length === 1); + + assert.equal(fourth[0].toString(), 'foo:b'); + assert.ok(!isFileChange(fourth)); + assert.ok(isTextChange(fourth) && fourth[1].length === 1); + }); test('DocumentLink', function () { assert.throws(() => new types.DocumentLink(null, null)); From 54ff9a1974b784df1d84b5b6f12664c145707b94 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 19 Jun 2018 16:55:36 +0200 Subject: [PATCH 055/149] delayed loading of vscode-textmate --- .../workbench/services/textMate/electron-browser/TMSyntax.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index b45a12e0ab0bf..b081a9e7e6dbb 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -14,7 +14,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ITokenizationSupport, TokenizationRegistry, IState, LanguageId, TokenMetadata } from 'vs/editor/common/modes'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { StackElement, IGrammar, Registry, IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, ITokenTypeMap, StandardTokenType, parseRawGrammar } from 'vscode-textmate'; +import { StackElement, IGrammar, Registry, IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, ITokenTypeMap, StandardTokenType } from 'vscode-textmate'; import { IWorkbenchThemeService, ITokenColorizationRule } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; import { grammarsExtPoint, IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint, TokenTypesContribution } from 'vs/workbench/services/textMate/electron-browser/TMGrammars'; @@ -207,7 +207,7 @@ export class TextMateService implements ITextMateService { private _getOrCreateGrammarRegistry(): TPromise<[Registry, StackElement]> { if (!this._grammarRegistry) { - this._grammarRegistry = TPromise.wrap(import('vscode-textmate')).then(({ Registry, INITIAL }) => { + this._grammarRegistry = TPromise.wrap(import('vscode-textmate')).then(({ Registry, INITIAL, parseRawGrammar }) => { const grammarRegistry = new Registry({ loadGrammar: (scopeName: string) => { const location = this._scopeRegistry.getGrammarLocation(scopeName); From ea28332e280af1087b3863166a6806b86f8198fb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 16:45:49 +0200 Subject: [PATCH 056/149] more testing on #10659 --- .../src/singlefolder-tests/workspace.test.ts | 6 ++---- .../bulkEdit/electron-browser/bulkEditService.ts | 11 ++--------- .../test/electron-browser/api/extHostTypes.test.ts | 12 ++++++++++++ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 77ed49227ca1b..1ef1c60cd4146 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -586,7 +586,7 @@ suite('workspace-namespace', () => { assert.ok(await vscode.workspace.applyEdit(we)); // todo@ben https://github.com/Microsoft/vscode/issues/52212 // let doc = await vscode.workspace.openTextDocument(newUri); - // assert.equal(doc.getText(), 'BarFooHello'); + // assert.equal(doc.getText(), 'BarHelloFoo'); }); test('WorkspaceEdit: Problem recreating a renamed resource #42634', async function () { @@ -605,9 +605,7 @@ suite('workspace-namespace', () => { // todo@ben https://github.com/Microsoft/vscode/issues/52212 // let newDoc = await vscode.workspace.openTextDocument(newUri); - // assert.equal(newDoc.getText(), 'FooHello'); - - // todo@ben https://github.com/Microsoft/vscode/issues/52212 + // assert.equal(newDoc.getText(), 'HelloFoo'); // let doc = await vscode.workspace.openTextDocument(docUri); // assert.equal(doc.getText(), 'Bar'); }); diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index f104e52bc7704..1d989c07002ce 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -27,6 +27,7 @@ import { emptyProgressRunner, IProgress, IProgressRunner } from 'vs/platform/pro import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { mergeSort } from 'vs/base/common/arrays'; abstract class Recording { @@ -91,15 +92,7 @@ class EditTask implements IDisposable { apply(): void { if (this._edits.length > 0) { - - this._edits = this._edits.map((value, index) => ({ value, index })).sort((a, b) => { - let ret = Range.compareRangesUsingStarts(a.value.range, b.value.range); - if (ret === 0) { - ret = a.index - b.index; - } - return ret; - }).map(element => element.value); - + this._edits = mergeSort(this._edits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); this._initialSelections = this._getInitialSelections(); this._model.pushStackElement(); this._model.pushEditOperations(this._initialSelections, this._edits, (edits) => this._getEndCursorSelections(edits)); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index 6a9037d6d82e2..11e5187b6e6f1 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -412,6 +412,18 @@ suite('ExtHostTypes', function () { assert.ok(isTextChange(fourth) && fourth[1].length === 1); }); + test('WorkspaceEdit - two edits for one resource', function () { + let edit = new types.WorkspaceEdit(); + let uri = URI.parse('foo:bar'); + edit.insert(uri, new types.Position(0, 0), 'Hello'); + edit.insert(uri, new types.Position(0, 0), 'Foo'); + + assert.equal(edit.allEntries().length, 2); + let [first, second] = edit.allEntries(); + assert.equal((first as [URI, types.TextEdit[]])[1][0].newText, 'Hello'); + assert.equal((second as [URI, types.TextEdit[]])[1][0].newText, 'Foo'); + }); + test('DocumentLink', function () { assert.throws(() => new types.DocumentLink(null, null)); assert.throws(() => new types.DocumentLink(new types.Range(1, 1, 1, 1), null)); From 29a7289ba32d2fdbdb7f54a43291704a1a1b9f39 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 16:57:45 +0200 Subject: [PATCH 057/149] add test for #42638 --- .../src/singlefolder-tests/workspace.test.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 1ef1c60cd4146..ddf664ff16365 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -656,4 +656,18 @@ suite('workspace-namespace', () => { // todo@ben https://github.com/Microsoft/vscode/issues/52212 // assert.equal(doc.getText(), 'Hello'); }); + + test('WorkspaceEdit: rename resource followed by edit does not work #42638', async function () { + let docUri = await createRandomFile(); + let newUri = nameWithUnderscore(docUri); + + let we = new vscode.WorkspaceEdit(); + we.renameFile(docUri, newUri); + we.insert(newUri, new vscode.Position(0, 0), 'Hello'); + + assert.ok(await vscode.workspace.applyEdit(we)); + + let doc = await vscode.workspace.openTextDocument(newUri); + assert.equal(doc.getText(), 'Hello'); + }); }); From 58da9d0bb1f9e7c1ac267b46645fa0be788eb44a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 19 Jun 2018 17:05:47 +0200 Subject: [PATCH 058/149] fix comment for #51695 --- src/vs/vscode.d.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 026f84b57b104..6b291af09fab7 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2540,16 +2540,17 @@ declare module 'vscode' { export interface WorkspaceSymbolProvider { /** - * Project-wide search for a symbol matching the given query string. It is up to the provider - * how to search given the query string, like substring, indexOf etc. To improve performance implementors can - * skip the [location](#SymbolInformation.location) of symbols and implement `resolveWorkspaceSymbol` to do that - * later. + * Project-wide search for a symbol matching the given query string. * * The `query`-parameter should be interpreted in a *relaxed way* as the editor will apply its own highlighting * and scoring on the results. A good rule of thumb is to match case-insensitive and to simply check that the * characters of *query* appear in their order in a candidate symbol. Don't use prefix, substring, or similar * strict matching. * + * To improve performance implementors can implement `resolveWorkspaceSymbol` and then provide symbols with partial + * [location](#SymbolInformation.location)-objects, without a `range` defined. The editor will then call + * `resolveWorkspaceSymbol` for selected symbols only, e.g. when opening a workspace symbol. + * * @param query A non-empty query string. * @param token A cancellation token. * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be From a0fe17d2d1b3c0b62a96311e6330ca01a8eea6d6 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 19 Jun 2018 17:10:44 +0200 Subject: [PATCH 059/149] JSONValidationExtensionPoint: use extensionLocation --- .../jsonschemas/common/jsonValidationExtensionPoint.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts b/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts index 38e859052baa0..d5ab87d54f749 100644 --- a/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts +++ b/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts @@ -6,9 +6,8 @@ import * as nls from 'vs/nls'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import URI from 'vs/base/common/uri'; import * as strings from 'vs/base/common/strings'; -import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; interface IJSONValidationExtensionPoint { fileMatch: string; @@ -60,8 +59,10 @@ export class JSONValidationExtensionPoint { } if (strings.startsWith(uri, './')) { try { - //TODO@extensionLocation - uri = URI.file(paths.normalize(paths.join(extensionLocation.fsPath, uri))).toString(); + const colorThemeLocation = resources.joinPath(extensionLocation, uri); + if (colorThemeLocation.path.indexOf(extensionLocation.path) !== 0) { + collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.url` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", configurationExtPoint.name, location, extensionLocation.path)); + } } catch (e) { collector.error(nls.localize('invalid.url.fileschema', "'configuration.jsonValidation.url' is an invalid relative URL: {0}", e.message)); } From e1f1b6cb7b851a1967f5f511c48bd9d708161a49 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 19 Jun 2018 17:23:54 +0200 Subject: [PATCH 060/149] disable actions and commands for readonly resources --- .../parts/files/common/explorerModel.ts | 1 + .../fileActions.contribution.ts | 19 ++++++++++++------- .../files/electron-browser/fileActions.ts | 12 ++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 3fd7f8d95460b..4b72173d75afb 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -194,6 +194,7 @@ export class ExplorerItem { local.mtime = disk.mtime; local.isDirectoryResolved = disk.isDirectoryResolved; local._isSymbolicLink = disk.isSymbolicLink; + local._isReadonly = disk.isReadonly; // Merge Children if resolved if (mergingDirectories && disk.isDirectoryResolved) { diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts index 59221606c5112..96443a0f6dcab 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -16,7 +16,7 @@ import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/c import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { isWindows, isMacintosh } from 'vs/base/common/platform'; -import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext } from 'vs/workbench/parts/files/common/files'; +import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceIsReadonlyContext } from 'vs/workbench/parts/files/common/files'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands'; import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { OPEN_FOLDER_SETTINGS_COMMAND, OPEN_FOLDER_SETTINGS_LABEL } from 'vs/workbench/parts/preferences/browser/preferencesActions'; @@ -330,7 +330,8 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { order: 4, command: { id: NEW_FILE_COMMAND_ID, - title: NEW_FILE_LABEL + title: NEW_FILE_LABEL, + precondition: ExplorerResourceIsReadonlyContext.toNegated() }, when: ExplorerFolderContext }); @@ -340,7 +341,8 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { order: 6, command: { id: NEW_FOLDER_COMMAND_ID, - title: NEW_FOLDER_LABEL + title: NEW_FOLDER_LABEL, + precondition: ExplorerResourceIsReadonlyContext.toNegated() }, when: ExplorerFolderContext }); @@ -443,7 +445,8 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { order: 10, command: { id: RENAME_ID, - title: TRIGGER_RENAME_LABEL + title: TRIGGER_RENAME_LABEL, + precondition: ExplorerResourceIsReadonlyContext.toNegated() }, when: ExplorerRootContext.toNegated() }); @@ -453,15 +456,17 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { order: 20, command: { id: MOVE_FILE_TO_TRASH_ID, - title: MOVE_FILE_TO_TRASH_LABEL + title: MOVE_FILE_TO_TRASH_LABEL, + precondition: ExplorerResourceIsReadonlyContext.toNegated() }, alt: { id: DELETE_FILE_ID, - title: nls.localize('deleteFile', "Delete Permanently") + title: nls.localize('deleteFile', "Delete Permanently"), + precondition: ExplorerResourceIsReadonlyContext.toNegated() }, when: ExplorerRootContext.toNegated() }); // Empty Editor Group Context Menu MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.files.newUntitledFile', title: nls.localize('newFile', "New File") }, group: '1_file', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.quickOpen', title: nls.localize('openFile', "Open File...") }, group: '1_file', order: 20 }); \ No newline at end of file +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.quickOpen', title: nls.localize('openFile', "Open File...") }, group: '1_file', order: 20 }); diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 9c1a8580d7831..4e1fea8954b8d 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -234,6 +234,10 @@ export abstract class BaseRenameAction extends BaseFileAction { this.element = element; } + _isEnabled(): boolean { + return super._isEnabled() && this.element && !this.element.isReadonly; + } + public run(context?: any): TPromise { if (!context) { return TPromise.wrapError(new Error('No context provided to BaseRenameFileAction.')); @@ -332,6 +336,10 @@ export class BaseNewAction extends BaseFileAction { this.renameAction = editableAction; } + _isEnabled(): boolean { + return super._isEnabled() && (!this.presetFolder || !this.presetFolder.isReadonly); + } + public run(context?: any): TPromise { if (!context) { return TPromise.wrapError(new Error('No context provided to BaseNewAction.')); @@ -547,6 +555,10 @@ class BaseDeleteFileAction extends BaseFileAction { this._updateEnablement(); } + _isEnabled(): boolean { + return super._isEnabled() && this.elements && this.elements.every(e => !e.isReadonly); + } + public run(): TPromise { // Remove highlight From dd7e8af40a973dfbe50cc5cc453c5e6abf4ef320 Mon Sep 17 00:00:00 2001 From: Isidor Nikolic Date: Tue, 19 Jun 2018 17:38:32 +0200 Subject: [PATCH 061/149] Open readonly files as readonly editors (#52337) * workbench: readonly files open as readonly editors * polish --- src/vs/workbench/browser/parts/editor/textDiffEditor.ts | 5 ++++- src/vs/workbench/common/editor/textDiffEditorModel.ts | 6 +++++- src/vs/workbench/common/editor/textEditorModel.ts | 4 ++++ .../workbench/parts/files/browser/editors/textFileEditor.ts | 2 ++ .../services/textfile/common/textFileEditorModel.ts | 6 +++++- src/vs/workbench/services/textfile/common/textfiles.ts | 2 ++ 6 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 74753bc36cf05..39fffebe8204e 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -118,7 +118,8 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { // Set Editor Model const diffEditor = this.getControl(); - diffEditor.setModel((resolvedModel).textDiffEditorModel); + const resolvedDiffEditorModel = resolvedModel; + diffEditor.setModel(resolvedDiffEditorModel.textDiffEditorModel); // Apply Options from TextOptions let optionsGotApplied = false; @@ -142,6 +143,8 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { this.previousDiffAction.updateEnablement(); })); + this.getControl().updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() }); + this.updateIgnoreTrimWhitespaceAction(); }, error => { diff --git a/src/vs/workbench/common/editor/textDiffEditorModel.ts b/src/vs/workbench/common/editor/textDiffEditorModel.ts index dc199593c220a..70c3279e8d8e0 100644 --- a/src/vs/workbench/common/editor/textDiffEditorModel.ts +++ b/src/vs/workbench/common/editor/textDiffEditorModel.ts @@ -66,6 +66,10 @@ export class TextDiffEditorModel extends DiffEditorModel { return !!this._textDiffEditorModel; } + public isReadonly(): boolean { + return this.modifiedModel.isReadonly(); + } + public dispose(): void { // Free the diff editor model but do not propagate the dispose() call to the two models @@ -76,4 +80,4 @@ export class TextDiffEditorModel extends DiffEditorModel { super.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 3d87656feb765..20bec65ef18bc 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -137,6 +137,10 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd return !!this.textEditorModelHandle; } + public isReadonly(): boolean { + return false; + } + public dispose(): void { if (this.modelDisposeListener) { this.modelDisposeListener.dispose(); // dispose this first because it will trigger another dispose() otherwise diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index de33f2ffaeb7d..22c66bc7cfdaa 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -146,6 +146,8 @@ export class TextFileEditor extends BaseTextEditor { if (options && types.isFunction((options).apply)) { (options).apply(textEditor, ScrollType.Immediate); } + + textEditor.updateOptions({ readOnly: textFileModel.isReadonly() }); }, error => { // In case we tried to open a file inside the text editor and the response diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index a625dbdba080f..9c0bdb25a4c7e 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -698,7 +698,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // be disposed if we are dirty, but if we are not dirty, save() and dispose() can still be triggered // one after the other without waiting for the save() to complete. If we are disposed(), we risk // saving contents to disk that are stale (see https://github.com/Microsoft/vscode/issues/50942). - // To fix this issue, we will not store the contents to disk when we got disposed. + // To fix this issue, we will not store the contents to disk when we got disposed. if (this.disposed) { return void 0; } @@ -983,6 +983,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return !types.isUndefinedOrNull(this.lastResolvedDiskStat); } + public isReadonly(): boolean { + return this.lastResolvedDiskStat.isReadonly; + } + /** * Returns true if the dispose() method of this model has been called. */ diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index a59d72866223c..724acf9bda7f4 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -232,6 +232,8 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport isResolved(): boolean; + isReadonly(): boolean; + isDisposed(): boolean; } From a032e74b88d6dbd4a07c1f4c07972acdcb24e7cf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 17:41:51 +0200 Subject: [PATCH 062/149] follow up: lastResolvedDiskStat can be null --- src/vs/workbench/browser/parts/editor/textDiffEditor.ts | 7 +++++-- .../parts/files/browser/editors/textFileEditor.ts | 1 + .../services/textfile/common/textFileEditorModel.ts | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 39fffebe8204e..79e9914076793 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -133,6 +133,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { hasPreviousViewState = this.restoreTextDiffEditorViewState(input); } + // Diff navigator this.diffNavigator = new DiffNavigator(diffEditor, { alwaysRevealFirst: !optionsGotApplied && !hasPreviousViewState // only reveal first change if we had no options or viewstate }); @@ -143,9 +144,11 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { this.previousDiffAction.updateEnablement(); })); - this.getControl().updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() }); - + // Enablement of actions this.updateIgnoreTrimWhitespaceAction(); + + // Readonly flag + diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() }); }, error => { // In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff. diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index 22c66bc7cfdaa..4b71f7e2b427e 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -147,6 +147,7 @@ export class TextFileEditor extends BaseTextEditor { (options).apply(textEditor, ScrollType.Immediate); } + // Readonly flag textEditor.updateOptions({ readOnly: textFileModel.isReadonly() }); }, error => { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 9c0bdb25a4c7e..b6dc162085208 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -984,7 +984,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } public isReadonly(): boolean { - return this.lastResolvedDiskStat.isReadonly; + return this.lastResolvedDiskStat && this.lastResolvedDiskStat.isReadonly; } /** From 6e088a71622b6dc0dfed5532f0d16e08e310b9b4 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 19 Jun 2018 17:45:11 +0200 Subject: [PATCH 063/149] Minor edits --- src/vs/base/parts/ipc/node/ipc.net.ts | 11 ++++++++--- src/vs/code/electron-main/app.ts | 4 ++-- .../api/electron-browser/mainThreadTerminalService.ts | 2 +- .../extensions/electron-browser/extensionHost.ts | 2 +- .../workbench/services/extensions/node/rpcProtocol.ts | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 499e8a52a06a6..fee0d49d4d862 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -31,7 +31,7 @@ export class Protocol implements IMessagePassingProtocol { readonly onMessage: Event = this._onMessage.event; - constructor(private _socket: Socket) { + constructor(private _socket: Socket, firstDataChunk?: Buffer) { let chunks: Buffer[] = []; let totalLength = 0; @@ -42,7 +42,7 @@ export class Protocol implements IMessagePassingProtocol { bodyLen: -1, }; - _socket.on('data', (data: Buffer) => { + const acceptChunk = (data: Buffer) => { chunks.push(data); totalLength += data.length; @@ -93,7 +93,12 @@ export class Protocol implements IMessagePassingProtocol { } } } - }); + }; + + if (firstDataChunk && firstDataChunk.length > 0) { + acceptChunk(firstDataChunk); + } + _socket.on('data', acceptChunk); } public send(message: any): void { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 964e1ed48981a..aebb464f8d21c 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -57,7 +57,7 @@ import { IIssueService } from 'vs/platform/issue/common/issue'; import { IssueChannel } from 'vs/platform/issue/common/issueIpc'; import { IssueService } from 'vs/platform/issue/electron-main/issueService'; import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; -import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; +import * as errors from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; @@ -97,7 +97,7 @@ export class CodeApplication { private registerListeners(): void { // We handle uncaught exceptions here to prevent electron from opening a dialog to the user - setUnexpectedErrorHandler(err => this.onUnexpectedError(err)); + errors.setUnexpectedErrorHandler(err => this.onUnexpectedError(err)); process.on('uncaughtException', err => this.onUnexpectedError(err)); app.on('will-quit', () => { diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index d953dcb6a8c0c..421daa0c9e4f3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -7,7 +7,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal'; import { TPromise } from 'vs/base/common/winjs.base'; -import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from '../node/extHost.protocol'; +import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 61547023c096a..c5943fb7778c8 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -361,7 +361,7 @@ export class ExtensionHostProcessWorker { } private _createExtHostInitData(): TPromise { - return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensions]).then(([telemetryInfo, extensionDescriptions]) => { + return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensions]).then(([telemetryInfo, extensionDescriptions]) => { const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: {} }; const r: IInitData = { parentPid: process.pid, diff --git a/src/vs/workbench/services/extensions/node/rpcProtocol.ts b/src/vs/workbench/services/extensions/node/rpcProtocol.ts index 6c38a0bbe90c6..6830dfa03c8d8 100644 --- a/src/vs/workbench/services/extensions/node/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/node/rpcProtocol.ts @@ -41,7 +41,7 @@ function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: n return null; } -function transformOutgoingURIs(obj: any, transformer: IURITransformer): any { +export function transformOutgoingURIs(obj: any, transformer: IURITransformer): any { const result = _transformOutgoingURIs(obj, transformer, 0); if (result === null) { // no change From aa3809481ba391dbe8d0dd41607bcbdab72588b4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 17:48:27 +0200 Subject: [PATCH 064/149] grid - fix bad overlay height value when dragging --- src/vs/workbench/browser/parts/editor/editorDropTarget.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index f5092c4881ddd..d2dafc0cfe3e4 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -356,6 +356,7 @@ class DropOverlay extends Themable { this.overlay.style.top = options.top; this.overlay.style.left = options.left; this.overlay.style.width = options.width; + this.overlay.style.height = options.height; } private getOverlayOffsetHeight(): number { From 83625fa9cbbdec3eab4a635c62f88aab0929aa80 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 18:13:03 +0200 Subject: [PATCH 065/149] debt - avoid async import when chained with WinJS promises --- .../files/electron-browser/fileService.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index e2021d010ce14..56cbb5ec49ab0 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -929,16 +929,15 @@ export class FileService implements IFileService { private doMoveItemToTrash(resource: uri): TPromise { const absolutePath = resource.fsPath; - return asWinJSImport(import('electron')).then(electron => { - const result = electron.shell.moveItemToTrash(absolutePath); - if (!result) { - return TPromise.wrapError(new Error(isWindows ? nls.localize('binFailed', "Failed to move '{0}' to the recycle bin", paths.basename(absolutePath)) : nls.localize('trashFailed', "Failed to move '{0}' to the trash", paths.basename(absolutePath)))); - } + const shell = (require.__$__nodeRequire('electron') as Electron.RendererInterface).shell; // workaround for being able to run tests out of VSCode debugger + const result = shell.moveItemToTrash(absolutePath); + if (!result) { + return TPromise.wrapError(new Error(isWindows ? nls.localize('binFailed', "Failed to move '{0}' to the recycle bin", paths.basename(absolutePath)) : nls.localize('trashFailed', "Failed to move '{0}' to the trash", paths.basename(absolutePath)))); + } - this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE)); + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE)); - return void 0; - }); + return TPromise.as(void 0); } private doDelete(resource: uri): TPromise { From 8729695a01ed61bf3b1c988833e89118189c71d3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 10:02:18 -0700 Subject: [PATCH 066/149] Update markdown grammar --- extensions/markdown-basics/syntaxes/markdown.tmLanguage.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 834bbfd0b1a0d..2838349491e8d 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/a8c8d497129a7c7dcac870cd49e9764436c15f22", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/fc14af6c0ca3c2bdaa1681f6f51834fe6a0a4279", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -1994,7 +1994,7 @@ "while": "(^|\\G)([ ]{4}|\\t)" }, "separator": { - "match": "(^|\\G)[ ]{0,3}([*-_])([ ]{0,2}\\2){2,}[ \\t]*$\\n?", + "match": "(^|\\G)[ ]{0,3}([\\*\\-\\_])([ ]{0,2}\\2){2,}[ \\t]*$\\n?", "name": "meta.separator.markdown" } } From 260ee3284e84ab114b4d054370f400e49e5a9065 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 10:02:37 -0700 Subject: [PATCH 067/149] Update js/ts grammars --- .../syntaxes/JavaScript.tmLanguage.json | 63 ++++++++++++++++--- .../syntaxes/JavaScriptReact.tmLanguage.json | 63 ++++++++++++++++--- .../syntaxes/TypeScript.tmLanguage.json | 63 ++++++++++++++++--- .../syntaxes/TypeScriptReact.tmLanguage.json | 63 ++++++++++++++++--- 4 files changed, 220 insertions(+), 32 deletions(-) diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 049257e3c67e7..30c1ca68b1c52 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/5629dc0e736de7e26b7f86db725bea2e3a67eb80", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/7bf8960f7042474b10b519f39339fc527907ce16", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -337,7 +337,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js entity.name.function.js" @@ -577,7 +577,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -809,7 +809,7 @@ "include": "#comment" }, { - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js entity.name.function.js" @@ -2536,11 +2536,11 @@ }, { "name": "keyword.operator.expression.in.js", - "match": "(?)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)))", + "captures": { + "1": { + "name": "storage.modifier.js" + }, + "2": { + "name": "keyword.operator.rest.js" + }, + "3": { + "name": "entity.name.function.js variable.language.this.js" + }, + "4": { + "name": "entity.name.function.js" + }, + "5": { + "name": "keyword.operator.optional.js" + } + } + }, + { + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx entity.name.function.js.jsx" @@ -577,7 +577,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -809,7 +809,7 @@ "include": "#comment" }, { - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js.jsx entity.name.function.js.jsx" @@ -2536,11 +2536,11 @@ }, { "name": "keyword.operator.expression.in.js.jsx", - "match": "(?)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)))", + "captures": { + "1": { + "name": "storage.modifier.js.jsx" + }, + "2": { + "name": "keyword.operator.rest.js.jsx" + }, + "3": { + "name": "entity.name.function.js.jsx variable.language.this.js.jsx" + }, + "4": { + "name": "entity.name.function.js.jsx" + }, + "5": { + "name": "keyword.operator.optional.js.jsx" + } + } + }, + { + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts entity.name.function.ts" @@ -574,7 +574,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -806,7 +806,7 @@ "include": "#comment" }, { - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.ts entity.name.function.ts" @@ -2570,11 +2570,11 @@ }, { "name": "keyword.operator.expression.in.ts", - "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)))", + "captures": { + "1": { + "name": "storage.modifier.ts" + }, + "2": { + "name": "keyword.operator.rest.ts" + }, + "3": { + "name": "entity.name.function.ts variable.language.this.ts" + }, + "4": { + "name": "entity.name.function.ts" + }, + "5": { + "name": "keyword.operator.optional.ts" + } + } + }, + { + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx entity.name.function.tsx" @@ -577,7 +577,7 @@ } }, { - "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -809,7 +809,7 @@ "include": "#comment" }, { - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.tsx entity.name.function.tsx" @@ -2536,11 +2536,11 @@ }, { "name": "keyword.operator.expression.in.tsx", - "match": "(?)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)))", + "captures": { + "1": { + "name": "storage.modifier.tsx" + }, + "2": { + "name": "keyword.operator.rest.tsx" + }, + "3": { + "name": "entity.name.function.tsx variable.language.this.tsx" + }, + "4": { + "name": "entity.name.function.tsx" + }, + "5": { + "name": "keyword.operator.optional.tsx" + } + } + }, + { + "match": "(?x)(?:(? Date: Tue, 19 Jun 2018 19:04:11 +0200 Subject: [PATCH 068/149] fix #52212 --- .../src/singlefolder-tests/workspace.test.ts | 18 ++-- .../workbench/electron-browser/workbench.ts | 8 +- .../parts/backup/common/backupModelTracker.ts | 3 - .../parts/backup/common/backupRestorer.ts | 8 +- .../services/backup/common/backup.ts | 5 - .../services/backup/node/backupFileService.ts | 94 +++++++++++++------ .../workspace/node/workspaceEditingService.ts | 5 +- .../workbench/test/workbenchTestServices.ts | 2 - 8 files changed, 83 insertions(+), 60 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index ddf664ff16365..407f8dbf2e545 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -564,9 +564,8 @@ suite('workspace-namespace', () => { let success = await vscode.workspace.applyEdit(edit); assert.equal(success, true); - // todo@ben https://github.com/Microsoft/vscode/issues/52212 - // let doc = await vscode.workspace.openTextDocument(newUri); - // assert.equal(doc.getText(), 'AFTERBEFORE'); + let doc = await vscode.workspace.openTextDocument(newUri); + assert.equal(doc.getText(), 'AFTERBEFORE'); }); function nameWithUnderscore(uri: vscode.Uri) { @@ -584,9 +583,8 @@ suite('workspace-namespace', () => { we.insert(newUri, new vscode.Position(0, 0), 'Bar'); assert.ok(await vscode.workspace.applyEdit(we)); - // todo@ben https://github.com/Microsoft/vscode/issues/52212 - // let doc = await vscode.workspace.openTextDocument(newUri); - // assert.equal(doc.getText(), 'BarHelloFoo'); + let doc = await vscode.workspace.openTextDocument(newUri); + assert.equal(doc.getText(), 'BarHelloFoo'); }); test('WorkspaceEdit: Problem recreating a renamed resource #42634', async function () { @@ -603,9 +601,8 @@ suite('workspace-namespace', () => { assert.ok(await vscode.workspace.applyEdit(we)); - // todo@ben https://github.com/Microsoft/vscode/issues/52212 - // let newDoc = await vscode.workspace.openTextDocument(newUri); - // assert.equal(newDoc.getText(), 'HelloFoo'); + let newDoc = await vscode.workspace.openTextDocument(newUri); + assert.equal(newDoc.getText(), 'HelloFoo'); // let doc = await vscode.workspace.openTextDocument(docUri); // assert.equal(doc.getText(), 'Bar'); }); @@ -653,8 +650,7 @@ suite('workspace-namespace', () => { let doc = await vscode.workspace.openTextDocument(newUri); assert.ok(doc); - // todo@ben https://github.com/Microsoft/vscode/issues/52212 - // assert.equal(doc.getText(), 'Hello'); + assert.equal(doc.getText(), 'Hello'); }); test('WorkspaceEdit: rename resource followed by edit does not work #42638', async function () { diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 1dd8ea0391a24..b53a4c8f1d035 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -17,7 +17,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import * as browser from 'vs/base/browser/browser'; import * as perf from 'vs/base/common/performance'; import * as errors from 'vs/base/common/errors'; -import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; +import { BackupFileService, InMemoryBackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; @@ -415,7 +415,11 @@ export class Workbench extends Disposable implements IPartService { this.menubarPart = this.instantiationService.createInstance(MenubarPart, Identifiers.MENUBAR_PART); // Backup File Service - this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.workbenchParams.configuration.backupPath); + if (this.workbenchParams.configuration.backupPath) { + this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.workbenchParams.configuration.backupPath); + } else { + this.backupFileService = new InMemoryBackupFileService(); + } serviceCollection.set(IBackupFileService, this.backupFileService); // Text File Service diff --git a/src/vs/workbench/parts/backup/common/backupModelTracker.ts b/src/vs/workbench/parts/backup/common/backupModelTracker.ts index 4cb9b8c767083..d4b296540d171 100644 --- a/src/vs/workbench/parts/backup/common/backupModelTracker.ts +++ b/src/vs/workbench/parts/backup/common/backupModelTracker.ts @@ -36,9 +36,6 @@ export class BackupModelTracker implements IWorkbenchContribution { } private registerListeners() { - if (!this.backupFileService.backupEnabled) { - return; - } // Listen for text file model changes this.toDispose.push(this.textFileService.models.onModelContentChanged((e) => this.onTextFileModelChanged(e))); diff --git a/src/vs/workbench/parts/backup/common/backupRestorer.ts b/src/vs/workbench/parts/backup/common/backupRestorer.ts index c9fba97e14bed..70b860ad8de44 100644 --- a/src/vs/workbench/parts/backup/common/backupRestorer.ts +++ b/src/vs/workbench/parts/backup/common/backupRestorer.ts @@ -35,11 +35,9 @@ export class BackupRestorer implements IWorkbenchContribution { } private restoreBackups(): void { - if (this.backupFileService.backupEnabled) { - this.lifecycleService.when(LifecyclePhase.Running).then(() => { - this.doRestoreBackups().done(null, errors.onUnexpectedError); - }); - } + this.lifecycleService.when(LifecyclePhase.Running).then(() => { + this.doRestoreBackups().done(null, errors.onUnexpectedError); + }); } private doRestoreBackups(): TPromise { diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index 86d5326107eee..be76ff6c4286a 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -22,11 +22,6 @@ export const BACKUP_FILE_UPDATE_OPTIONS: IUpdateContentOptions = { encoding: 'ut export interface IBackupFileService { _serviceBrand: any; - /** - * If backups are enabled. - */ - backupEnabled: boolean; - /** * Finds out if there are any backups stored. */ diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index fdeea689752f0..8b2c585dc4962 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -15,7 +15,8 @@ import { IFileService, ITextSnapshot } from 'vs/platform/files/common/files'; import { TPromise } from 'vs/base/common/winjs.base'; import { readToMatchingString } from 'vs/base/node/stream'; import { ITextBufferFactory } from 'vs/editor/common/model'; -import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; +import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; +import { keys } from 'vs/base/common/map'; export interface IBackupFilesModel { resolve(backupRoot: string): TPromise; @@ -135,17 +136,9 @@ export class BackupFileService implements IBackupFileService { this.ready = this.init(); } - public get backupEnabled(): boolean { - return !!this.backupWorkspacePath; // Hot exit requires a backup path - } - private init(): TPromise { const model = new BackupFilesModel(); - if (!this.backupEnabled) { - return TPromise.as(model); - } - return model.resolve(this.backupWorkspacePath); } @@ -157,12 +150,9 @@ export class BackupFileService implements IBackupFileService { public loadBackupResource(resource: Uri): TPromise { return this.ready.then(model => { - const backupResource = this.toBackupResource(resource); - if (!backupResource) { - return void 0; - } // Return directly if we have a known backup with that resource + const backupResource = this.toBackupResource(resource); if (model.has(backupResource)) { return backupResource; } @@ -178,10 +168,6 @@ export class BackupFileService implements IBackupFileService { return this.ready.then(model => { const backupResource = this.toBackupResource(resource); - if (!backupResource) { - return void 0; - } - if (model.has(backupResource, versionId)) { return void 0; // return early if backup version id matches requested one } @@ -198,9 +184,6 @@ export class BackupFileService implements IBackupFileService { public discardResourceBackup(resource: Uri): TPromise { return this.ready.then(model => { const backupResource = this.toBackupResource(resource); - if (!backupResource) { - return void 0; - } return this.ioOperationQueues.queueFor(backupResource).queue(() => { return pfs.del(backupResource.fsPath).then(() => model.remove(backupResource)); @@ -212,10 +195,6 @@ export class BackupFileService implements IBackupFileService { this.isShuttingDown = true; return this.ready.then(model => { - if (!this.backupEnabled) { - return void 0; - } - return pfs.del(this.backupWorkspacePath).then(() => model.clear()); }); } @@ -226,8 +205,7 @@ export class BackupFileService implements IBackupFileService { model.get().forEach(fileBackup => { readPromises.push( - readToMatchingString(fileBackup.fsPath, BackupFileService.META_MARKER, 2000, 10000) - .then(Uri.parse) + readToMatchingString(fileBackup.fsPath, BackupFileService.META_MARKER, 2000, 10000).then(Uri.parse) ); }); @@ -259,10 +237,6 @@ export class BackupFileService implements IBackupFileService { } public toBackupResource(resource: Uri): Uri { - if (!this.backupEnabled) { - return null; - } - return Uri.file(path.join(this.backupWorkspacePath, resource.scheme, this.hashPath(resource))); } @@ -270,3 +244,63 @@ export class BackupFileService implements IBackupFileService { return crypto.createHash('md5').update(resource.fsPath).digest('hex'); } } + +export class InMemoryBackupFileService implements IBackupFileService { + + public _serviceBrand: any; + + private backups: Map = new Map(); + + hasBackups(): TPromise { + return TPromise.as(this.backups.size > 0); + } + + loadBackupResource(resource: Uri): TPromise { + const backupResource = this.toBackupResource(resource); + if (this.backups.has(backupResource.toString())) { + return TPromise.as(backupResource); + } + + return TPromise.as(void 0); + } + + backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): TPromise { + const backupResource = this.toBackupResource(resource); + this.backups.set(backupResource.toString(), content); + + return TPromise.as(void 0); + } + + resolveBackupContent(backupResource: Uri): TPromise { + const snapshot = this.backups.get(backupResource.toString()); + if (snapshot) { + return TPromise.as(createTextBufferFactoryFromSnapshot(snapshot)); + } + + return TPromise.as(void 0); + } + + getWorkspaceFileBackups(): TPromise { + return TPromise.as(keys(this.backups).map(key => Uri.parse(key))); + } + + discardResourceBackup(resource: Uri): TPromise { + this.backups.delete(this.toBackupResource(resource).toString()); + + return TPromise.as(void 0); + } + + discardAllWorkspaceBackups(): TPromise { + this.backups.clear(); + + return TPromise.as(void 0); + } + + toBackupResource(resource: Uri): Uri { + return Uri.file(path.join(resource.scheme, this.hashPath(resource))); + } + + private hashPath(resource: Uri): string { + return crypto.createHash('md5').update(resource.fsPath).digest('hex'); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 5e8f0fb6ca9a8..b9c500f1c5ac4 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -202,8 +202,9 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { // TODO@Ben TODO@Sandeep the following requires ugly casts and should probably have a service interface // Reinitialize backup service - const backupFileService = this.backupFileService as BackupFileService; - backupFileService.initialize(result.backupPath); + if (this.backupFileService instanceof BackupFileService) { + this.backupFileService.initialize(result.backupPath); + } // Reinitialize configuration service const workspaceImpl = this.contextService as WorkspaceService; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 461dcf4daf815..7348c180e156d 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -871,8 +871,6 @@ export class TestFileService implements IFileService { export class TestBackupFileService implements IBackupFileService { public _serviceBrand: any; - public backupEnabled: boolean; - public hasBackups(): TPromise { return TPromise.as(false); } From 0977a4c3866a108dc93be6a643dabdee54bc2cf1 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Tue, 19 Jun 2018 10:08:56 -0700 Subject: [PATCH 069/149] initialize title in custom titlebar --- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index a7dd3d25003d6..e7d44d9beb20e 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -83,6 +83,8 @@ export class TitlebarPart extends Part implements ITitleService { this.properties = { isPure: true, isAdmin: false }; this.activeEditorListeners = []; + this.setTitle(this.getWindowTitle()); + this.registerListeners(); } From e8008a7953ef7abdadd01341c98bb7d4a69b3078 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Jun 2018 19:10:41 +0200 Subject: [PATCH 070/149] grid - tweak autoFocus in quick pick depending on active editor group being empty or not --- .../workbench/browser/parts/quickopen/quickOpenController.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index da8b3a5045467..59e9137b08ac5 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -101,7 +101,6 @@ export class QuickOpenController extends Component implements IQuickOpenService private editorHistoryHandler: EditorHistoryHandler; constructor( - @IEditorService private editorService: IEditorService, @IEditorGroupsService private editorGroupService: IEditorGroupsService, @INotificationService private notificationService: INotificationService, @IContextKeyService private contextKeyService: IContextKeyService, @@ -507,8 +506,8 @@ export class QuickOpenController extends Component implements IQuickOpenService if (!quickNavigateConfiguration) { autoFocus = { autoFocusFirstEntry: true }; } else { - const visibleEditorCount = this.editorService.visibleEditors.length; - autoFocus = { autoFocusFirstEntry: visibleEditorCount === 0, autoFocusSecondEntry: visibleEditorCount !== 0 }; + const autoFocusFirstEntry = this.editorGroupService.activeGroup.count === 0; + autoFocus = { autoFocusFirstEntry, autoFocusSecondEntry: !autoFocusFirstEntry }; } } From 88904d72696fd93e6b35864e0adb4434df3341dc Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Tue, 19 Jun 2018 10:39:57 -0700 Subject: [PATCH 071/149] fixes $52275 --- src/vs/workbench/browser/parts/menubar/menubarPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/menubar/menubarPart.ts b/src/vs/workbench/browser/parts/menubar/menubarPart.ts index ab7402fff28f5..6ecc50082dc13 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/menubar/menubarPart.ts @@ -369,7 +369,7 @@ export class MenubarPart extends Part { titleElement.attr('role', 'menu'); let mnemonic = (/&&(.)/g).exec(this.topLevelTitles[menuTitle])[1]; - if (mnemonic) { + if (mnemonic && this.currentEnableMenuBarMnemonics) { this.registerMnemonic(titleElement.getHTMLElement(), mnemonic); } From 76f33a4ab649e216c9af65ed5c83c6ba47b5b304 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 19 Jun 2018 19:59:37 +0200 Subject: [PATCH 072/149] Separete scanning and filtering extensions --- .../electron-browser/extensionService.ts | 191 ++++++++++-------- 1 file changed, 102 insertions(+), 89 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index f9a6fafae8161..4c8fae279e7a9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -42,6 +42,7 @@ import * as strings from 'vs/base/common/strings'; import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { Schemas } from 'vs/base/common/network'; let _SystemExtensionsRoot: string = null; function getSystemExtensionsRoot(): string { @@ -486,119 +487,131 @@ export class ExtensionService extends Disposable implements IExtensionService { private _scanAndHandleExtensions(): void { - this._getRuntimeExtensions().then(allExtensions => { - this._registry = new ExtensionDescriptionRegistry(allExtensions); + this._scanExtensions() + .then(allExtensions => this._getRuntimeExtensions(allExtensions)) + .then(allExtensions => { + this._registry = new ExtensionDescriptionRegistry(allExtensions); - let availableExtensions = this._registry.getAllExtensionDescriptions(); - let extensionPoints = ExtensionsRegistry.getExtensionPoints(); + let availableExtensions = this._registry.getAllExtensionDescriptions(); + let extensionPoints = ExtensionsRegistry.getExtensionPoints(); - let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); + let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); - for (let i = 0, len = extensionPoints.length; i < len; i++) { - ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); - } + for (let i = 0, len = extensionPoints.length; i < len; i++) { + ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); + } - mark('extensionHostReady'); - this._installedExtensionsReady.open(); - this._onDidRegisterExtensions.fire(void 0); - this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.id)); - }); + mark('extensionHostReady'); + this._installedExtensionsReady.open(); + this._onDidRegisterExtensions.fire(void 0); + this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.id)); + }); } - private _getRuntimeExtensions(): TPromise { + private _scanExtensions(): TPromise { const log = new Logger((severity, source, message) => { this._logOrShowMessage(severity, this._isDev ? messageWithSource(source, message) : message); }); return ExtensionService._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, log) .then(({ system, user, development }) => { - return this._extensionEnablementService.getDisabledExtensions() - .then(disabledExtensions => { - let result: { [extensionId: string]: IExtensionDescription; } = {}; - let extensionsToDisable: IExtensionIdentifier[] = []; - let userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }]; - - system.forEach((systemExtension) => { - if (disabledExtensions.every(disabled => !areSameExtensions(disabled, systemExtension))) { - result[systemExtension.id] = systemExtension; - } - }); + let result: { [extensionId: string]: IExtensionDescription; } = {}; + system.forEach((systemExtension) => { + result[systemExtension.id] = systemExtension; + }); + user.forEach((userExtension) => { + if (result.hasOwnProperty(userExtension.id)) { + log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); + } + result[userExtension.id] = userExtension; + }); + development.forEach(developedExtension => { + log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionLocation.fsPath)); + if (result.hasOwnProperty(developedExtension.id)) { + log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionLocation.fsPath, developedExtension.extensionLocation.fsPath)); + } + result[developedExtension.id] = developedExtension; + }); + return Object.keys(result).map(name => result[name]); + }); + } - user.forEach((userExtension) => { - if (result.hasOwnProperty(userExtension.id)) { - log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); - } - if (disabledExtensions.every(disabled => !areSameExtensions(disabled, userExtension))) { - // Check if the extension is changed to system extension - let userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: userExtension.id }))[0]; - if (userMigratedSystemExtension) { - extensionsToDisable.push(userMigratedSystemExtension); - } else { - result[userExtension.id] = userExtension; - } - } - }); + private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): TPromise { + return this._extensionEnablementService.getDisabledExtensions() + .then(disabledExtensions => { - development.forEach(developedExtension => { - log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionLocation.fsPath)); - if (result.hasOwnProperty(developedExtension.id)) { - log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionLocation.fsPath, developedExtension.extensionLocation.fsPath)); - } - // Do not disable extensions under development - result[developedExtension.id] = developedExtension; - }); + const result: { [extensionId: string]: IExtensionDescription; } = {}; + const extensionsToDisable: IExtensionIdentifier[] = []; + const userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }]; - const runtimeExtensions = Object.keys(result).map(name => result[name]); + const enableProposedApiForAll = !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0); + const enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || []; - this._telemetryService.publicLog('extensionsScanned', { - totalCount: runtimeExtensions.length, - disabledCount: disabledExtensions.length - }); + for (const extension of allExtensions) { + const isExtensionUnderDevelopment = this._environmentService.isExtensionDevelopment && extension.extensionLocation.scheme === Schemas.file && extension.extensionLocation.fsPath.indexOf(this._environmentService.extensionDevelopmentPath) === 0; + // Do not disable extensions under development + if (!isExtensionUnderDevelopment) { + if (disabledExtensions.some(disabled => areSameExtensions(disabled, extension))) { + continue; + } + } - if (extensionsToDisable.length) { - return this.extensionManagementService.getInstalled(LocalExtensionType.User) - .then(installed => { - const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e))); - return TPromise.join(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); - }) - .then(() => { - this._storageService.store(BetterMergeDisabledNowKey, true); - return runtimeExtensions; - }); - } else { - return runtimeExtensions; + if (!extension.isBuiltin) { + // Check if the extension is changed to system extension + const userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: extension.id }))[0]; + if (userMigratedSystemExtension) { + extensionsToDisable.push(userMigratedSystemExtension); + continue; } - }); - }).then(extensions => this._updateEnableProposedApi(extensions)); - } + } + result[extension.id] = this._updateEnableProposedApi(extension, enableProposedApiForAll, enableProposedApiFor); + } + const runtimeExtensions = Object.keys(result).map(name => result[name]); - private _updateEnableProposedApi(extensions: IExtensionDescription[]): IExtensionDescription[] { - const enableProposedApiForAll = !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0); - const enableProposedApiFor = this._environmentService.args['enable-proposed-api'] || []; - for (const extension of extensions) { - if (!isFalsyOrEmpty(product.extensionAllowedProposedApi) - && product.extensionAllowedProposedApi.indexOf(extension.id) >= 0 - ) { - // fast lane -> proposed api is available to all extensions - // that are listed in product.json-files - extension.enableProposedApi = true; - - } else if (extension.enableProposedApi && !extension.isBuiltin) { - if ( - !enableProposedApiForAll && - enableProposedApiFor.indexOf(extension.id) < 0 - ) { - extension.enableProposedApi = false; - console.error(`Extension '${extension.id} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); + this._telemetryService.publicLog('extensionsScanned', { + totalCount: runtimeExtensions.length, + disabledCount: disabledExtensions.length + }); + if (extensionsToDisable.length) { + return this.extensionManagementService.getInstalled(LocalExtensionType.User) + .then(installed => { + const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e))); + return TPromise.join(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); + }) + .then(() => { + this._storageService.store(BetterMergeDisabledNowKey, true); + return runtimeExtensions; + }); } else { - // proposed api is available when developing or when an extension was explicitly - // spelled out via a command line argument - console.warn(`Extension '${extension.id}' uses PROPOSED API which is subject to change and removal without notice.`); + return runtimeExtensions; } + }); + } + + private _updateEnableProposedApi(extension: IExtensionDescription, enableProposedApiForAll: boolean, enableProposedApiFor: string | string[]): IExtensionDescription { + if (!isFalsyOrEmpty(product.extensionAllowedProposedApi) + && product.extensionAllowedProposedApi.indexOf(extension.id) >= 0 + ) { + // fast lane -> proposed api is available to all extensions + // that are listed in product.json-files + extension.enableProposedApi = true; + + } else if (extension.enableProposedApi && !extension.isBuiltin) { + if ( + !enableProposedApiForAll && + enableProposedApiFor.indexOf(extension.id) < 0 + ) { + extension.enableProposedApi = false; + console.error(`Extension '${extension.id} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); + + } else { + // proposed api is available when developing or when an extension was explicitly + // spelled out via a command line argument + console.warn(`Extension '${extension.id}' uses PROPOSED API which is subject to change and removal without notice.`); } } - return extensions; + return extension; } private _handleExtensionPointMessage(msg: IMessage) { From 4d3a8ae210376ccbed854ffbe0364e33adcffef3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 10:57:11 -0700 Subject: [PATCH 073/149] Add tag closing completions Part of #34307. Add manual completions for closing jsx tags. Requires TS 3.0 --- .../src/features/tagCompletion.ts | 66 +++++++++++++++++++ .../src/languageProvider.ts | 1 + .../src/protocol.d.ts | 2 +- .../src/typescriptService.ts | 6 ++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 extensions/typescript-language-features/src/features/tagCompletion.ts diff --git a/extensions/typescript-language-features/src/features/tagCompletion.ts b/extensions/typescript-language-features/src/features/tagCompletion.ts new file mode 100644 index 0000000000000..8a8495180ca37 --- /dev/null +++ b/extensions/typescript-language-features/src/features/tagCompletion.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import * as typeConverters from '../utils/typeConverters'; + +class TypeScriptTagCompletion implements vscode.CompletionItemProvider { + constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + async provideCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + _context: vscode.CompletionContext + ): Promise { + const filepath = this.client.toPath(document.uri); + if (!filepath) { + return undefined; + } + + const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + let body: Proto.TextInsertion | undefined = undefined; + try { + const response = await this.client.execute('jsxClosingTag', args, token); + body = response && response.body; + if (!body) { + return undefined; + } + } catch { + return undefined; + } + + return [this.getCompletion(body)]; + } + + private getCompletion(body: Proto.TextInsertion) { + const completion = new vscode.CompletionItem(body.newText); + completion.insertText = this.getTagSnippet(body); + return completion; + } + + private getTagSnippet(closingTag: Proto.TextInsertion): vscode.SnippetString { + const snippet = new vscode.SnippetString(); + snippet.appendPlaceholder('', 0); + snippet.appendText(closingTag.newText); + return snippet; + } +} + +export function register( + selector: vscode.DocumentSelector, + client: ITypeScriptServiceClient, +) { + return new VersionDependentRegistration(client, API.v300, () => + vscode.languages.registerCompletionItemProvider(selector, + new TypeScriptTagCompletion(client), + '>')); +} diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index a00f7e9a47045..1776fd1b8fc5b 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -91,6 +91,7 @@ export default class LanguageProvider { this.disposables.push((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); this.disposables.push((await import('./features/rename')).register(selector, this.client)); this.disposables.push((await import('./features/signatureHelp')).register(selector, this.client)); + this.disposables.push((await import('./features/tagCompletion')).register(selector, this.client)); this.disposables.push((await import('./features/typeDefinitions')).register(selector, this.client)); this.disposables.push((await import('./features/workspaceSymbols')).register(this.client, this.description.modeIds)); } diff --git a/extensions/typescript-language-features/src/protocol.d.ts b/extensions/typescript-language-features/src/protocol.d.ts index 0015b80d9276e..6e926eb8d7ee2 100644 --- a/extensions/typescript-language-features/src/protocol.d.ts +++ b/extensions/typescript-language-features/src/protocol.d.ts @@ -1,2 +1,2 @@ import * as Proto from 'typescript/lib/protocol'; -export = Proto; \ No newline at end of file +export = Proto; diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index ef58af1e6350a..8c5b240f2acb1 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -11,6 +11,11 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import Logger from './utils/logger'; import BufferSyncSupport from './features/bufferSyncSupport'; +declare module './protocol' { + export type JsxClosingTagRequestArgs = any; + export type JsxClosingTagResponse = any; +} + export interface ITypeScriptServiceClient { /** * Convert a resource (VS Code) to a normalized path (TypeScript). @@ -78,6 +83,7 @@ export interface ITypeScriptServiceClient { execute(command: 'organizeImports', args: Proto.OrganizeImportsRequestArgs, token?: CancellationToken): Promise; execute(command: 'getOutliningSpans', args: Proto.FileRequestArgs, token: CancellationToken): Promise; execute(command: 'getEditsForFileRename', args: Proto.GetEditsForFileRenameRequestArgs): Promise; + execute(command: 'jsxClosingTag', args: Proto.JsxClosingTagRequestArgs, token: CancellationToken): Promise; execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise; executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: CancellationToken): Promise; From f71c37848884363ed1bc9c3c6c88684c46197c94 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 11:06:14 -0700 Subject: [PATCH 074/149] Make sure we trigger js/ts completions on / --- .../src/features/completions.ts | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index ff9eb608df63c..1943dd881b903 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -449,42 +449,44 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider line: vscode.TextLine, position: vscode.Position ): boolean { - if ((context.triggerCharacter === '"' || context.triggerCharacter === '\'') && !this.client.apiVersion.gte(API.v290)) { - if (!config.quickSuggestionsForPaths) { - return false; - } + if (context.triggerCharacter && !this.client.apiVersion.gte(API.v290)) { + if ((context.triggerCharacter === '"' || context.triggerCharacter === '\'')) { + if (!config.quickSuggestionsForPaths) { + return false; + } - // make sure we are in something that looks like the start of an import - const pre = line.text.slice(0, position.character); - if (!pre.match(/\b(from|import)\s*["']$/) && !pre.match(/\b(import|require)\(['"]$/)) { - return false; + // make sure we are in something that looks like the start of an import + const pre = line.text.slice(0, position.character); + if (!pre.match(/\b(from|import)\s*["']$/) && !pre.match(/\b(import|require)\(['"]$/)) { + return false; + } } - } - if (context.triggerCharacter === '/') { - if (!config.quickSuggestionsForPaths) { - return false; + if (context.triggerCharacter === '/') { + if (!config.quickSuggestionsForPaths) { + return false; + } + + // make sure we are in something that looks like an import path + const pre = line.text.slice(0, position.character); + if (!pre.match(/\b(from|import)\s*["'][^'"]*$/) && !pre.match(/\b(import|require)\(['"][^'"]*$/)) { + return false; + } } - // make sure we are in something that looks like an import path - const pre = line.text.slice(0, position.character); - if (!pre.match(/\b(from|import)\s*["'][^'"]*$/) && !pre.match(/\b(import|require)\(['"][^'"]*$/)) { - return false; + if (context.triggerCharacter === '@') { + // make sure we are in something that looks like the start of a jsdoc comment + const pre = line.text.slice(0, position.character); + if (!pre.match(/^\s*\*[ ]?@/) && !pre.match(/\/\*\*+[ ]?@/)) { + return false; + } } - } - if (context.triggerCharacter === '@' && !this.client.apiVersion.gte(API.v290)) { - // make sure we are in something that looks like the start of a jsdoc comment - const pre = line.text.slice(0, position.character); - if (!pre.match(/^\s*\*[ ]?@/) && !pre.match(/\/\*\*+[ ]?@/)) { + if (context.triggerCharacter === '<') { return false; } } - if (context.triggerCharacter === '<') { - return this.client.apiVersion.gte(API.v290); - } - return true; } From 344f4d3a6ac9240c141c1280a47d578e2aecbbea Mon Sep 17 00:00:00 2001 From: Alfonso Perez Date: Tue, 19 Jun 2018 11:12:17 -0700 Subject: [PATCH 075/149] Keep deleting lines when triggering deleteAllLeft on column 1 (#28392) * keep deleting lines when doing a deleteAllLeft on column 1 --- .../linesOperations/linesOperations.ts | 27 ++++-- .../test/linesOperations.test.ts | 95 +++++++++++++++++++ 2 files changed, 116 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index 0e2e13b66b6e1..d6d79bee280b2 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -408,8 +408,8 @@ export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction { effectiveRanges.push(rangesToDelete[rangesToDelete.length - 1]); let endCursorState = this._getEndCursorState(primaryCursor, effectiveRanges); + let edits: IIdentifiedSingleEditOperation[] = effectiveRanges.map(range => { - endCursorState.push(new Selection(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn)); return EditOperation.replace(range, ''); }); @@ -444,17 +444,25 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction { _getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] { let endPrimaryCursor: Selection; let endCursorState: Selection[] = []; + let deletedLines = 0; - for (let i = 0, len = rangesToDelete.length; i < len; i++) { - let range = rangesToDelete[i]; - let endCursor = new Selection(rangesToDelete[i].startLineNumber, rangesToDelete[i].startColumn, rangesToDelete[i].startLineNumber, rangesToDelete[i].startColumn); + rangesToDelete.forEach(range => { + let endCursor; + if (range.endColumn === 1 && deletedLines > 0) { + let newStartLine = range.startLineNumber - deletedLines; + endCursor = new Selection(newStartLine, range.startColumn, newStartLine, range.startColumn); + } else { + endCursor = new Selection(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn); + } + + deletedLines += range.endLineNumber - range.startLineNumber; if (range.intersectRanges(primaryCursor)) { endPrimaryCursor = endCursor; } else { endCursorState.push(endCursor); } - } + }); if (endPrimaryCursor) { endCursorState.unshift(endPrimaryCursor); @@ -465,11 +473,18 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction { _getRangesToDelete(editor: ICodeEditor): Range[] { let rangesToDelete: Range[] = editor.getSelections(); + let model = editor.getModel(); rangesToDelete.sort(Range.compareRangesUsingStarts); rangesToDelete = rangesToDelete.map(selection => { if (selection.isEmpty()) { - return new Range(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn); + if (selection.startColumn === 1) { + let deleteFromLine = Math.max(1, selection.startLineNumber - 1); + let deleteFromColumn = selection.startLineNumber === 1 ? 1 : model.getLineContent(deleteFromLine).length + 1; + return new Range(deleteFromLine, deleteFromColumn, selection.startLineNumber, 1); + } else { + return new Range(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn); + } } else { return selection; } diff --git a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts index ae01a2682eb91..31963d459865d 100644 --- a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts @@ -155,6 +155,101 @@ suite('Editor Contrib - Line Operations', () => { }); }); + test('should jump to the previous line when on first column', function () { + withTestCodeEditor( + [ + 'one', + 'two', + 'three' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let deleteAllLeftAction = new DeleteAllLeftAction(); + + editor.setSelection(new Selection(2, 1, 2, 1)); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(1), 'onetwo', '001'); + + editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLinesContent()[0], 'onetwothree'); + assert.equal(model.getLinesContent().length, 1); + + editor.setSelection(new Selection(1, 1, 1, 1)); + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLinesContent()[0], 'onetwothree'); + }); + }); + + test('should keep deleting lines in multi cursor mode', function () { + withTestCodeEditor( + [ + 'hi my name is Carlos Matos', + 'BCC', + 'waso waso waso', + 'my wife doesnt believe in me', + 'nonononono', + 'bitconneeeect' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let deleteAllLeftAction = new DeleteAllLeftAction(); + + const beforeSecondWasoSelection = new Selection(3, 5, 3, 5); + const endOfBCCSelection = new Selection(2, 4, 2, 4); + const endOfNonono = new Selection(5, 11, 5, 11); + + editor.setSelections([beforeSecondWasoSelection, endOfBCCSelection, endOfNonono]); + let selections; + + deleteAllLeftAction.run(null, editor); + selections = editor.getSelections(); + + assert.equal(model.getLineContent(2), ''); + assert.equal(model.getLineContent(3), ' waso waso'); + assert.equal(model.getLineContent(5), ''); + + assert.deepEqual([ + selections[0].startLineNumber, + selections[0].startColumn, + selections[0].endLineNumber, + selections[0].endColumn + ], [3, 1, 3, 1]); + + assert.deepEqual([ + selections[1].startLineNumber, + selections[1].startColumn, + selections[1].endLineNumber, + selections[1].endColumn + ], [2, 1, 2, 1]); + + assert.deepEqual([ + selections[2].startLineNumber, + selections[2].startColumn, + selections[2].endLineNumber, + selections[2].endColumn + ], [5, 1, 5, 1]); + + deleteAllLeftAction.run(null, editor); + selections = editor.getSelections(); + + assert.equal(model.getLineContent(1), 'hi my name is Carlos Matos waso waso'); + assert.equal(selections.length, 2); + + assert.deepEqual([ + selections[0].startLineNumber, + selections[0].startColumn, + selections[0].endLineNumber, + selections[0].endColumn + ], [1, 27, 1, 27]); + + assert.deepEqual([ + selections[1].startLineNumber, + selections[1].startColumn, + selections[1].endLineNumber, + selections[1].endColumn + ], [2, 29, 2, 29]); + }); + }); + test('should work in multi cursor mode', function () { withTestCodeEditor( [ From dcffd79899fdc946b92fc05b4172c989fa49a565 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Tue, 19 Jun 2018 11:19:41 -0700 Subject: [PATCH 076/149] removing invalid classes from extension actions --- .../parts/extensions/electron-browser/extensionsActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 7e8ddea0e51f5..10957613b75ad 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -1147,7 +1147,7 @@ export class ShowEnabledExtensionsAction extends Action { label: string, @IViewletService private viewletService: IViewletService ) { - super(id, label, 'clear-extensions', true); + super(id, label, null, true); } run(): TPromise { @@ -1170,7 +1170,7 @@ export class ShowInstalledExtensionsAction extends Action { label: string, @IViewletService private viewletService: IViewletService ) { - super(id, label, 'clear-extensions', true); + super(id, label, null, true); } run(): TPromise { From 0e361509ef77c8116afd08b66c816a168f215593 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 11:46:51 -0700 Subject: [PATCH 077/149] Fix completion characters inside of js function call Fixes #52350 --- .../typescript-language-features/src/features/completions.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index 1943dd881b903..e26463d69b3b5 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -51,7 +51,6 @@ class MyCompletionItem extends vscode.CompletionItem { this.position = position; this.commitCharacters = MyCompletionItem.getCommitCharacters(enableDotCompletions, !useCodeSnippetsOnMethodSuggest, tsEntry.kind); this.useCodeSnippet = useCodeSnippetsOnMethodSuggest && (this.kind === vscode.CompletionItemKind.Function || this.kind === vscode.CompletionItemKind.Method); - if (tsEntry.replacementSpan) { this.range = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan); } @@ -437,7 +436,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider const preText = document.getText(new vscode.Range( position.line, 0, position.line, position.character - 1)); - return preText.match(/[a-z_$\)\]\}]\s*$/ig) !== null; + return preText.match(/[a-z_$\(\)\[\]\{\}]\s*$/ig) !== null; } return true; From e4fe04ad5bac6b31ab78f8044591f6ab6eca7d4a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 11:51:09 -0700 Subject: [PATCH 078/149] Treat comma as a commit character in js / ts Fixes #52351 --- .../src/features/completions.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index e26463d69b3b5..c7ce7bf6e56a8 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -148,6 +148,7 @@ class MyCompletionItem extends vscode.CompletionItem { enableCallCompletions: boolean, kind: string ): string[] | undefined { + const commitCharacters: string[] = []; switch (kind) { case PConst.Kind.memberGetAccessor: case PConst.Kind.memberSetAccessor: @@ -156,7 +157,10 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.indexSignature: case PConst.Kind.enum: case PConst.Kind.interface: - return enableDotCompletions ? ['.'] : undefined; + if (enableDotCompletions) { + commitCharacters.push('.'); + } + break; case PConst.Kind.module: case PConst.Kind.alias: @@ -168,10 +172,16 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.class: case PConst.Kind.function: case PConst.Kind.memberFunction: - return enableDotCompletions ? (enableCallCompletions ? ['.', '('] : ['.']) : undefined; + if (enableDotCompletions) { + commitCharacters.push('.', ','); + } + if (enableCallCompletions) { + commitCharacters.push('('); + } + break; } - return undefined; + return commitCharacters.length === 0 ? undefined : commitCharacters; } } From 442e5e202a89c1f8efe642c89c0554cab1f15a9e Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 19 Jun 2018 11:53:23 -0700 Subject: [PATCH 079/149] Add ability to ignore recommendations both globally and at a per-workspace level (#51941) * WIP * Initail filtering and pulling filter list logic. * Imporive typing and naming * Remove the significant duplication * Bug fixes. * Fix bug where arrow functions dont maintain `this` in point free style * WIP on extension ignoring UI. * UI for global ignores. * Add "unwantedRecommendations" to extensions.json template and intelisense * Notify of workspace non-recommended extensions * Wording of extensions.json template * More UI for ignore button. * Use seprate notification channel of recommendation changes. * Reload search when recommended extensions changes * Tests for ExtensionTipsService * Test extensions workbench service * Naming and add default vaule to workspace settings * Initial revisions * Global ignore need not call workspace refresh * Fix build issues * Skip refreshing workspace twice * WIP * WIP * Reduce file accesses. Remove bug causing all open editors to show exrtension as ignored * Fix some of the build issues. * Hackish thing that fixes the test. * Rename id to extensionId in RecommendationChangeNotification * updateRecommendedness ?? * Not needed * Remove point free style thing * Simplify * naming * remove extrenous getWrokspaceRecommendations call * Casing * Gracefull handle missing 'extensions' field in multiroot project config * Refresh recommendation views on recommendation change * more case sensitivity stuff * naming/refactroing * Simplify return types * Wording * Add telemetry * guard telemetry service --- .../common/extensionManagement.ts | 18 + src/vs/platform/node/product.ts | 2 +- .../common/extensionsFileTemplate.ts | 18 +- .../electron-browser/extensionEditor.ts | 47 +- .../electron-browser/extensionTipsService.ts | 409 +++++++++++------- .../electron-browser/extensionsActions.ts | 30 ++ .../electron-browser/extensionsList.ts | 29 +- .../electron-browser/extensionsViewlet.ts | 4 +- .../electron-browser/extensionsViews.ts | 45 +- .../media/extensionActions.css | 25 ++ .../media/extensionEditor.css | 20 +- .../extensionsTipsService.test.ts | 183 +++++++- 12 files changed, 619 insertions(+), 211 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 0a35c0f0cd144..12e6500741873 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -376,6 +376,21 @@ export interface IExtensionEnablementService { setEnablement(extension: ILocalExtension, state: EnablementState): TPromise; } +export interface IIgnoredRecommendations { + global: string[]; + workspace: string[]; +} + +export interface IExtensionsConfigContent { + recommendations: string[]; + unwantedRecommendations: string[]; +} + +export type RecommendationChangeNotification = { + extensionId: string, + isRecommended: boolean +}; + export const IExtensionTipsService = createDecorator('extensionTipsService'); export interface IExtensionTipsService { @@ -387,6 +402,9 @@ export interface IExtensionTipsService { getKeymapRecommendations(): string[]; getKeywordsForExtension(extension: string): string[]; getRecommendationsForExtension(extension: string): string[]; + getAllIgnoredRecommendations(): IIgnoredRecommendations; + ignoreExtensionRecommendation(extensionId: string): void; + onRecommendationChange: Event; } export enum ExtensionRecommendationReason { diff --git a/src/vs/platform/node/product.ts b/src/vs/platform/node/product.ts index 685ccc5d3740b..c60c4d01ec620 100644 --- a/src/vs/platform/node/product.ts +++ b/src/vs/platform/node/product.ts @@ -30,7 +30,7 @@ export interface IProductConfiguration { }; extensionTips: { [id: string]: string; }; extensionImportantTips: { [id: string]: { name: string; pattern: string; }; }; - exeBasedExtensionTips: { [id: string]: any; }; + exeBasedExtensionTips: { [id: string]: { friendlyName: string, windowsPath?: string, recommendations: string[] }; }; extensionKeywords: { [extension: string]: string[]; }; extensionAllowedBadgeProviders: string[]; extensionAllowedProposedApi: string[]; diff --git a/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts b/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts index fc81cb063d1b9..a6d988dfb7ccb 100644 --- a/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts +++ b/src/vs/workbench/parts/extensions/common/extensionsFileTemplate.ts @@ -13,10 +13,20 @@ export const ExtensionsConfigurationSchema: IJSONSchema = { allowComments: true, type: 'object', title: localize('app.extensions.json.title', "Extensions"), + additionalProperties: false, properties: { recommendations: { type: 'array', - description: localize('app.extensions.json.recommendations', "List of extensions recommendations. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'."), + description: localize('app.extensions.json.recommendations', "List of extensions which should be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'."), + items: { + type: 'string', + pattern: EXTENSION_IDENTIFIER_PATTERN, + errorMessage: localize('app.extension.identifier.errorMessage', "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.") + }, + }, + unwantedRecommendations: { + type: 'array', + description: localize('app.extensions.json.unwantedRecommendations', "List of extensions that will be skipped from the recommendations that VS Code makes for the users of this workspace. These are extensions that you may consider to be irrelevant, redundant, or otherwise unwanted. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'."), items: { type: 'string', pattern: EXTENSION_IDENTIFIER_PATTERN, @@ -31,6 +41,12 @@ export const ExtensionsConfigurationInitialContent: string = [ '\t// See http://go.microsoft.com/fwlink/?LinkId=827846', '\t// for the documentation about the extensions.json format', '\t"recommendations": [', + '\t\t// List of extensions which should be recommended for users of this workspace.', + '\t\t// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp', + '\t\t', + '\t],', + '\t"unwantedRecommendations": [', + '\t\t// List of extensions that will be skipped from the recommendations that VS Code makes for the users of this workspace. These are extensions that you may consider to be irrelevant, redundant, or otherwise unwanted.', '\t\t// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp', '\t\t', '\t]', diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index e42c43d374906..3440e74a6ddcf 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -31,7 +31,7 @@ import { Renderer, DataSource, Controller } from 'vs/workbench/parts/extensions/ import { RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, ReloadAction, MaliciousStatusLabelAction, DisabledStatusLabelAction, MultiServerInstallAction, MultiServerUpdateAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, ReloadAction, MaliciousStatusLabelAction, DisabledStatusLabelAction, MultiServerInstallAction, MultiServerUpdateAction, IgnoreExtensionRecommendationAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { WebviewElement } from 'vs/workbench/parts/webview/electron-browser/webviewElement'; import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -164,6 +164,8 @@ export class ExtensionEditor extends BaseEditor { private navbar: NavBar; private content: HTMLElement; private recommendation: HTMLElement; + private recommendationText: any; + private ignoreActionbar: ActionBar; private header: HTMLElement; private extensionReadme: Cache; @@ -256,15 +258,24 @@ export class ExtensionEditor extends BaseEditor { return null; } }); - this.disposables.push(this.extensionActionBar); this.recommendation = append(details, $('.recommendation')); + this.recommendationText = append(this.recommendation, $('.recommendation-text')); + this.ignoreActionbar = new ActionBar(this.recommendation, { animated: false }); + + this.disposables.push(this.extensionActionBar); + this.disposables.push(this.ignoreActionbar); chain(this.extensionActionBar.onDidRun) .map(({ error }) => error) .filter(error => !!error) .on(this.onError, this, this.disposables); + chain(this.ignoreActionbar.onDidRun) + .map(({ error }) => error) + .filter(error => !!error) + .on(this.onError, this, this.disposables); + const body = append(root, $('.body')); this.navbar = new NavBar(body); @@ -295,24 +306,25 @@ export class ExtensionEditor extends BaseEditor { this.publisher.textContent = extension.publisherDisplayName; this.description.textContent = extension.description; + removeClass(this.header, 'recommendation-ignored'); const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); let recommendationsData = {}; if (extRecommendations[extension.id.toLowerCase()]) { addClass(this.header, 'recommended'); - this.recommendation.textContent = extRecommendations[extension.id.toLowerCase()].reasonText; + this.recommendationText.textContent = extRecommendations[extension.id.toLowerCase()].reasonText; recommendationsData = { recommendationReason: extRecommendations[extension.id.toLowerCase()].reasonId }; } else { removeClass(this.header, 'recommended'); - this.recommendation.textContent = ''; + this.recommendationText.textContent = ''; } /* __GDPR__ - "extensionGallery:openExtension" : { - "recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "${include}": [ - "${GalleryExtensionTelemetryData}" - ] - } + "extensionGallery:openExtension" : { + "recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "${include}": [ + "${GalleryExtensionTelemetryData}" + ] + } */ this.telemetryService.publicLog('extensionGallery:openExtension', assign(extension.telemetryData, recommendationsData)); @@ -376,6 +388,21 @@ export class ExtensionEditor extends BaseEditor { this.extensionActionBar.push([disabledStatusAction, reloadAction, updateAction, enableAction, disableAction, installAction, maliciousStatusAction], { icon: true, label: true }); this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, maliciousStatusAction, disabledStatusAction); + const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction); + ignoreAction.extension = extension; + + this.extensionTipsService.onRecommendationChange(change => { + if (change.extensionId.toLowerCase() === extension.id.toLowerCase() && change.isRecommended === false) { + addClass(this.header, 'recommendation-ignored'); + removeClass(this.header, 'recommended'); + this.recommendationText.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); + } + }); + + this.ignoreActionbar.clear(); + this.ignoreActionbar.push([ignoreAction], { icon: true, label: true }); + this.transientDisposables.push(ignoreAction); + this.content.innerHTML = ''; // Clear content before setting navbar actions. this.navbar.clear(); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 71cce68cb0bb5..9c3312d585eb3 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -10,7 +10,7 @@ import { forEach } from 'vs/base/common/collections'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { match } from 'vs/base/common/glob'; import * as json from 'vs/base/common/json'; -import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, ExtensionRecommendationReason, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, ExtensionRecommendationReason, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN, IIgnoredRecommendations, IExtensionsConfigContent, RecommendationChangeNotification } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextModel } from 'vs/editor/common/model'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -36,10 +36,7 @@ import { asJson } from 'vs/base/node/request'; import { isNumber } from 'vs/base/common/types'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { INotificationService } from 'vs/platform/notification/common/notification'; - -interface IExtensionsContent { - recommendations: string[]; -} +import { Emitter, Event } from 'vs/base/common/event'; const empty: { [key: string]: any; } = Object.create(null); const milliSecondsInADay = 1000 * 60 * 60 * 24; @@ -52,6 +49,15 @@ interface IDynamicWorkspaceRecommendations { recommendations: string[]; } +function caseInsensitiveGet(obj: { [key: string]: T }, key: string): T | undefined { + for (const _key in obj) { + if (obj.hasOwnProperty(_key) && _key.toLowerCase() === key.toLowerCase()) { + return obj[_key]; + } + } + return undefined; +} + export class ExtensionTipsService extends Disposable implements IExtensionTipsService { _serviceBrand: any; @@ -61,11 +67,17 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe private _availableRecommendations: { [pattern: string]: string[] } = Object.create(null); private _allWorkspaceRecommendedExtensions: string[] = []; private _dynamicWorkspaceRecommendations: string[] = []; + private _allIgnoredRecommendations: string[] = []; + private _globallyIgnoredRecommendations: string[] = []; + private _workspaceIgnoredRecommendations: string[] = []; private _extensionsRecommendationsUrl: string; private _disposables: IDisposable[] = []; - public promptWorkspaceRecommendationsPromise: TPromise; + public loadRecommendationsPromise: TPromise; private proactiveRecommendationsFetched: boolean = false; + private readonly _onRecommendationChange: Emitter = new Emitter(); + onRecommendationChange: Event = this._onRecommendationChange.event; + constructor( @IExtensionGalleryService private readonly _galleryService: IExtensionGalleryService, @IModelService private readonly _modelService: IModelService, @@ -92,9 +104,19 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe this._extensionsRecommendationsUrl = product.extensionsGallery.recommendationsUrl; } - this.getCachedDynamicWorkspaceRecommendations(); - this._suggestFileBasedRecommendations(); - this.promptWorkspaceRecommendationsPromise = this._suggestWorkspaceRecommendations(); + let globallyIgnored = JSON.parse(this.storageService.get('extensionsAssistant/ignored_recommendations', StorageScope.GLOBAL, '[]')); + this._globallyIgnoredRecommendations = globallyIgnored.map(id => id.toLowerCase()); + + this.loadRecommendationsPromise = this.getWorkspaceRecommendations() + .then(() => { + // these must be called after workspace configs have been refreshed. + this.getCachedDynamicWorkspaceRecommendations(); + this._suggestFileBasedRecommendations(); + return this._suggestWorkspaceRecommendations(); + }).then(() => { + this._modelService.onModelAdded(this._suggest, this, this._disposables); + this._modelService.getModels().forEach(model => this._suggest(model)); + }); if (!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { this.fetchProactiveRecommendations(true); @@ -164,41 +186,69 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } getWorkspaceRecommendations(): TPromise { - if (!this.isEnabled()) { - return TPromise.as([]); - } - const workspace = this.contextService.getWorkspace(); - return TPromise.join([this.resolveWorkspaceRecommendations(workspace), ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderRecommendations(workspaceFolder))]) - .then(recommendations => { - this._allWorkspaceRecommendedExtensions = distinct(flatten(recommendations)); + if (!this.isEnabled) { return TPromise.as([]); } + + return this.fetchCombinedExtensionRecommendationConfig() + .then(content => { + this._workspaceIgnoredRecommendations = content.unwantedRecommendations; + this._allIgnoredRecommendations = distinct([...this._globallyIgnoredRecommendations, ...this._workspaceIgnoredRecommendations]); + this._allWorkspaceRecommendedExtensions = content.recommendations; + this.refilterAllRecommendations(); return this._allWorkspaceRecommendedExtensions; }); } - private resolveWorkspaceRecommendations(workspace: IWorkspace): TPromise { - if (workspace.configuration) { - return this.fileService.resolveContent(workspace.configuration) - .then(content => this.processWorkspaceRecommendations(json.parse(content.value, [])['extensions']), err => []); + private fetchCombinedExtensionRecommendationConfig(): TPromise { + const mergeExtensionRecommendationConfigs: (configs: IExtensionsConfigContent[]) => IExtensionsConfigContent = configs => ({ + recommendations: distinct(flatten(configs.map(config => config && config.recommendations || []))), + unwantedRecommendations: distinct(flatten(configs.map(config => config && config.unwantedRecommendations || []))) + }); + + const workspace = this.contextService.getWorkspace(); + return TPromise.join([this.resolveWorkspaceExtensionConfig(workspace), ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder))]) + .then(contents => this.processConfigContent(mergeExtensionRecommendationConfigs(contents))); + } + + private resolveWorkspaceExtensionConfig(workspace: IWorkspace): TPromise { + if (!workspace.configuration) { + return TPromise.as(null); } - return TPromise.as([]); + + return this.fileService.resolveContent(workspace.configuration) + .then(content => (json.parse(content.value)['extensions']), err => null); } - private resolveWorkspaceFolderRecommendations(workspaceFolder: IWorkspaceFolder): TPromise { + private resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): TPromise { const extensionsJsonUri = workspaceFolder.toResource(paths.join('.vscode', 'extensions.json')); - return this.fileService.resolveFile(extensionsJsonUri).then(() => { - return this.fileService.resolveContent(extensionsJsonUri) - .then(content => this.processWorkspaceRecommendations(json.parse(content.value, [])), err => []); - }, err => []); + + return this.fileService.resolveFile(extensionsJsonUri) + .then(() => this.fileService.resolveContent(extensionsJsonUri)) + .then(content => json.parse(content.value), err => null); } - private processWorkspaceRecommendations(extensionsContent: IExtensionsContent): TPromise { + private processConfigContent(extensionsContent: IExtensionsConfigContent): TPromise { + if (!extensionsContent) { + return TPromise.as({ recommendations: [], unwantedRecommendations: [] }); + } + const regEx = new RegExp(EXTENSION_IDENTIFIER_PATTERN); - if (extensionsContent && extensionsContent.recommendations && extensionsContent.recommendations.length) { - let countBadRecommendations = 0; - let badRecommendationsString = ''; - let filteredRecommendations = extensionsContent.recommendations.filter((element, position) => { - if (extensionsContent.recommendations.indexOf(element) !== position) { + let countBadRecommendations = 0; + let badRecommendationsString = ''; + let errorsNotification = () => { + if (countBadRecommendations > 0 && this.notificationService) { + this.notificationService.warn( + 'The below ' + + countBadRecommendations + + ' extension(s) in workspace recommendations have issues:\n' + + badRecommendationsString + ); + } + }; + + let regexFilter = (ids: string[]) => { + return ids.filter((element, position) => { + if (ids.indexOf(element) !== position) { // This is a duplicate entry, it doesn't hurt anybody // but it shouldn't be sent in the gallery query return false; @@ -207,52 +257,76 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe badRecommendationsString += `${element} (bad format) Expected: .\n`; return false; } - return true; }); + }; + + let filteredWanted = regexFilter(extensionsContent.recommendations || []).map(x => x.toLowerCase()); + let filteredUnwanted = regexFilter(extensionsContent.unwantedRecommendations || []).map(x => x.toLowerCase()); + + if (!filteredWanted.length) { + errorsNotification(); + return TPromise.as({ recommendations: filteredWanted, unwantedRecommendations: filteredUnwanted }); + } + + return this._galleryService.query({ names: filteredWanted }).then(pager => { + let page = pager.firstPage; + let validRecommendations = page.map(extension => { + return extension.identifier.id.toLowerCase(); + }); - return this._galleryService.query({ names: filteredRecommendations }).then(pager => { - let page = pager.firstPage; - let validRecommendations = page.map(extension => { - return extension.identifier.id.toLowerCase(); + if (validRecommendations.length !== filteredWanted.length) { + filteredWanted.forEach(element => { + if (validRecommendations.indexOf(element.toLowerCase()) === -1) { + countBadRecommendations++; + badRecommendationsString += `${element} (not found in marketplace)\n`; + } }); + } - if (validRecommendations.length !== filteredRecommendations.length) { - filteredRecommendations.forEach(element => { - if (validRecommendations.indexOf(element.toLowerCase()) === -1) { - countBadRecommendations++; - badRecommendationsString += `${element} (not found in marketplace)\n`; - } - }); - } + errorsNotification(); + return { recommendations: validRecommendations, unwantedRecommendations: filteredUnwanted }; + }); + } - if (countBadRecommendations > 0 && this.notificationService) { - this.notificationService.warn( - 'The below ' + - countBadRecommendations + - ' extension(s) in workspace recommendations have issues:\n' + - badRecommendationsString - ); - } + private refilterAllRecommendations() { + this._allWorkspaceRecommendedExtensions = this._allWorkspaceRecommendedExtensions.filter((id) => this.isExtensionAllowedToBeRecommended(id)); + this._dynamicWorkspaceRecommendations = this._dynamicWorkspaceRecommendations.filter((id) => this.isExtensionAllowedToBeRecommended(id)); - return validRecommendations; - }); + this._allIgnoredRecommendations.forEach(x => { + delete this._fileBasedRecommendations[x]; + delete this._exeBasedRecommendations[x]; + }); + + if (this._availableRecommendations) { + for (const key in this._availableRecommendations) { + if (Object.prototype.hasOwnProperty.call(this._availableRecommendations, key)) { + this._availableRecommendations[key] = this._availableRecommendations[key].filter(id => this.isExtensionAllowedToBeRecommended(id)); + } + } } + } - return TPromise.as([]); + getAllIgnoredRecommendations(): IIgnoredRecommendations { + return { + workspace: this._workspaceIgnoredRecommendations, + global: this._globallyIgnoredRecommendations + }; + } + private isExtensionAllowedToBeRecommended(id: string): boolean { + return this._allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; } private onWorkspaceFoldersChanged(event: IWorkspaceFoldersChangeEvent): void { if (event.added.length) { - TPromise.join(event.added.map(workspaceFolder => this.resolveWorkspaceFolderRecommendations(workspaceFolder))) - .then(result => { - const newRecommendations = flatten(result); - // Suggest only if atleast one of the newly added recommendtations was not suggested before - if (newRecommendations.some(e => this._allWorkspaceRecommendedExtensions.indexOf(e) === -1)) { - this._suggestWorkspaceRecommendations(); - } - }); + const oldWorkspaceRecommended = this._allWorkspaceRecommendedExtensions; + this.getWorkspaceRecommendations().then(result => { + // Suggest only if at least one of the newly added recommendations was not suggested before + if (result.some(e => oldWorkspaceRecommended.indexOf(e) === -1)) { + this._suggestWorkspaceRecommendations(); + } + }); } this._dynamicWorkspaceRecommendations = []; } @@ -261,10 +335,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const fileBased = Object.keys(this._fileBasedRecommendations) .sort((a, b) => { if (this._fileBasedRecommendations[a] === this._fileBasedRecommendations[b]) { - if (!product.extensionImportantTips || product.extensionImportantTips[a]) { + if (!product.extensionImportantTips || caseInsensitiveGet(product.extensionImportantTips, a)) { return -1; } - if (product.extensionImportantTips[b]) { + if (caseInsensitiveGet(product.extensionImportantTips, b)) { return 1; } } @@ -295,29 +369,30 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe this._availableRecommendations = Object.create(null); forEach(extensionTips, entry => { let { key: id, value: pattern } = entry; - let ids = this._availableRecommendations[pattern]; - if (!ids) { - this._availableRecommendations[pattern] = [id]; - } else { - ids.push(id); + if (this.isExtensionAllowedToBeRecommended(id)) { + let ids = this._availableRecommendations[pattern]; + if (!ids) { + this._availableRecommendations[pattern] = [id.toLowerCase()]; + } else { + ids.push(id.toLowerCase()); + } } }); forEach(product.extensionImportantTips, entry => { let { key: id, value } = entry; - const { pattern } = value; - let ids = this._availableRecommendations[pattern]; - if (!ids) { - this._availableRecommendations[pattern] = [id]; - } else { - ids.push(id); + if (this.isExtensionAllowedToBeRecommended(id)) { + const { pattern } = value; + let ids = this._availableRecommendations[pattern]; + if (!ids) { + this._availableRecommendations[pattern] = [id.toLowerCase()]; + } else { + ids.push(id.toLowerCase()); + } } }); - const allRecommendations: string[] = []; - forEach(this._availableRecommendations, ({ value: ids }) => { - allRecommendations.push(...ids); - }); + const allRecommendations: string[] = flatten((Object.keys(this._availableRecommendations).map(key => this._availableRecommendations[key]))); // retrieve ids of previous recommendations const storedRecommendationsJson = JSON.parse(this.storageService.get('extensionsAssistant/recommendations', StorageScope.GLOBAL, '[]')); @@ -325,7 +400,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (Array.isArray(storedRecommendationsJson)) { for (let id of storedRecommendationsJson) { if (allRecommendations.indexOf(id) > -1) { - this._fileBasedRecommendations[id] = Date.now(); + this._fileBasedRecommendations[id.toLowerCase()] = Date.now(); } } } else { @@ -334,14 +409,11 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (typeof entry.value === 'number') { const diff = (now - entry.value) / milliSecondsInADay; if (diff <= 7 && allRecommendations.indexOf(entry.key) > -1) { - this._fileBasedRecommendations[entry.key] = entry.value; + this._fileBasedRecommendations[entry.key.toLowerCase()] = entry.value; } } }); } - - this._modelService.onModelAdded(this._suggest, this, this._disposables); - this._modelService.getModels().forEach(model => this._suggest(model)); } private getMimeTypes(path: string): TPromise { @@ -370,12 +442,16 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe // the critical path - in case glob-match is slow setImmediate(() => { + let recommendationsToSuggest: string[] = []; const now = Date.now(); forEach(this._availableRecommendations, entry => { let { key: pattern, value: ids } = entry; if (match(pattern, uri.fsPath)) { for (let id of ids) { - this._fileBasedRecommendations[id] = now; + if (Object.keys(product.extensionImportantTips || []).map(x => x.toLowerCase()).indexOf(id.toLowerCase()) > -1) { + recommendationsToSuggest.push(id); + } + this._fileBasedRecommendations[id.toLowerCase()] = now; } } }); @@ -392,8 +468,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } const importantRecommendationsIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]')); - let recommendationsToSuggest = Object.keys(product.extensionImportantTips || []) - .filter(id => importantRecommendationsIgnoreList.indexOf(id) === -1 && match(product.extensionImportantTips[id]['pattern'], uri.fsPath)); + recommendationsToSuggest = recommendationsToSuggest.filter(id => importantRecommendationsIgnoreList.indexOf(id) === -1); const importantTipsPromise = recommendationsToSuggest.length === 0 ? TPromise.as(null) : this.extensionsService.getInstalled(LocalExtensionType.User).then(local => { recommendationsToSuggest = recommendationsToSuggest.filter(id => local.every(local => `${local.manifest.publisher}.${local.manifest.name}` !== id)); @@ -401,7 +476,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return; } const id = recommendationsToSuggest[0]; - const name = product.extensionImportantTips[id]['name']; + const name = caseInsensitiveGet(product.extensionImportantTips, id)['name']; // Indicates we have a suggested extension via the whitelist hasSuggestion = true; @@ -555,86 +630,85 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }); } - private _suggestWorkspaceRecommendations(): TPromise { + private _suggestWorkspaceRecommendations(): void { + const allRecommendations = this._allWorkspaceRecommendedExtensions; const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; const config = this.configurationService.getValue(ConfigurationKey); - return this.getWorkspaceRecommendations().then(allRecommendations => { - if (!allRecommendations.length || config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) { - return; + if (!allRecommendations.length || config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) { + return; + } + + return this.extensionsService.getInstalled(LocalExtensionType.User).done(local => { + const recommendations = allRecommendations + .filter(id => local.every(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}` !== id)); + + if (!recommendations.length) { + return TPromise.as(void 0); } - return this.extensionsService.getInstalled(LocalExtensionType.User).done(local => { - const recommendations = allRecommendations - .filter(id => local.every(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}` !== id)); + return new TPromise(c => { + this.notificationService.prompt( + Severity.Info, + localize('workspaceRecommended', "This workspace has extension recommendations."), + [{ + label: localize('installAll', "Install All"), + run: () => { + /* __GDPR__ + "extensionWorkspaceRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); - if (!recommendations.length) { - return TPromise.as(void 0); - } + const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, localize('installAll', "Install All")); + installAllAction.run(); + installAllAction.dispose(); - return new TPromise(c => { - this.notificationService.prompt( - Severity.Info, - localize('workspaceRecommended', "This workspace has extension recommendations."), - [{ - label: localize('installAll', "Install All"), - run: () => { - /* __GDPR__ + c(void 0); + } + }, { + label: localize('showRecommendations', "Show Recommendations"), + run: () => { + /* __GDPR__ "extensionWorkspaceRecommendations:popup" : { "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); - - const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, localize('installAll', "Install All")); - installAllAction.run(); - installAllAction.dispose(); - - c(void 0); - } - }, { - label: localize('showRecommendations', "Show Recommendations"), - run: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); - - const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); - showAction.run(); - showAction.dispose(); + */ + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); - c(void 0); - } - }, { - label: choiceNever, - isSecondary: true, - run: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); - this.storageService.store(storageKey, true, StorageScope.WORKSPACE); + const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); + showAction.run(); + showAction.dispose(); - c(void 0); - } - }], - () => { + c(void 0); + } + }, { + label: choiceNever, + isSecondary: true, + run: () => { /* __GDPR__ "extensionWorkspaceRecommendations:popup" : { "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); + this.storageService.store(storageKey, true, StorageScope.WORKSPACE); c(void 0); } - ); - }); + }], + () => { + /* __GDPR__ + "extensionWorkspaceRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); + + c(void 0); + } + ); }); }); } @@ -662,9 +736,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (exists && !foundExecutables.has(exeName)) { foundExecutables.add(exeName); (product.exeBasedExtensionTips[exeName]['recommendations'] || []) - .forEach(x => { - if (product.exeBasedExtensionTips[exeName]['friendlyName']) { - this._exeBasedRecommendations[x] = product.exeBasedExtensionTips[exeName]['friendlyName']; + .forEach(extensionId => { + if (product.exeBasedExtensionTips[exeName]['friendlyName'] && this.isExtensionAllowedToBeRecommended(extensionId)) { + this._exeBasedRecommendations[extensionId.toLowerCase()] = product.exeBasedExtensionTips[exeName]['friendlyName']; } }); } @@ -723,7 +797,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe && isNumber(storedRecommendationsJson['timestamp']) && storedRecommendationsJson['timestamp'] > 0 && (Date.now() - storedRecommendationsJson['timestamp']) / milliSecondsInADay < 14) { - this._dynamicWorkspaceRecommendations = storedRecommendationsJson['recommendations']; + this._dynamicWorkspaceRecommendations = storedRecommendationsJson['recommendations'].filter(id => this.isExtensionAllowedToBeRecommended(id)); /* __GDPR__ "dynamicWorkspaceRecommendations" : { "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -764,7 +838,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe for (let j = 0; j < allRecommendations.length && !foundRemote; j++) { if (Array.isArray(allRecommendations[j].remoteSet) && allRecommendations[j].remoteSet.indexOf(hashedRemotes[i]) > -1) { foundRemote = true; - this._dynamicWorkspaceRecommendations = allRecommendations[j].recommendations || []; + this._dynamicWorkspaceRecommendations = allRecommendations[j].recommendations.filter(id => this.isExtensionAllowedToBeRecommended(id)) || []; this.storageService.store(storageKey, JSON.stringify({ recommendations: this._dynamicWorkspaceRecommendations, timestamp: Date.now() @@ -812,6 +886,31 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return Object.keys(result); } + ignoreExtensionRecommendation(extensionId: string): void { + /* __GDPR__ + "extensionsRecommendations:ignoreRecommendation" : { + "recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + const reason = this.getAllRecommendationsWithReason()[extensionId.toLowerCase()]; + if (reason && reason.reasonId) { + this.telemetryService.publicLog('extensionsRecommendations:ignoreRecommendation', { id: extensionId, recommendationReason: reason.reasonId }); + } + + + this._globallyIgnoredRecommendations = distinct( + [...JSON.parse(this.storageService.get('extensionsAssistant/ignored_recommendations', StorageScope.GLOBAL, '[]')), extensionId.toLowerCase()] + .map(id => id.toLowerCase())); + + this.storageService.store('extensionsAssistant/ignored_recommendations', JSON.stringify(this._globallyIgnoredRecommendations), StorageScope.GLOBAL); + + this._allIgnoredRecommendations = distinct([...this._globallyIgnoredRecommendations, ...this._workspaceIgnoredRecommendations]); + + this.refilterAllRecommendations(); + this._onRecommendationChange.fire({ extensionId: extensionId, isRecommended: false }); + } + dispose() { this._disposables = dispose(this._disposables); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 10957613b75ad..e98faebeb0034 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -1488,6 +1488,36 @@ export class InstallRecommendedExtensionAction extends Action { } } +export class IgnoreExtensionRecommendationAction extends Action { + + static readonly ID = 'extensions.ignore'; + + private static readonly Class = 'extension-action ignore octicon octicon-x'; + + private disposables: IDisposable[] = []; + extension: IExtension; + + constructor( + @IExtensionTipsService private extensionsTipsService: IExtensionTipsService, + ) { + super(IgnoreExtensionRecommendationAction.ID); + + this.class = IgnoreExtensionRecommendationAction.Class; + this.tooltip = localize('ignoreExtensionRecommendation', "Do not recommend this extension again"); + this.enabled = true; + } + + public run(): TPromise { + this.extensionsTipsService.ignoreExtensionRecommendation(this.extension.id); + return TPromise.as(null); + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + export class ShowRecommendedKeymapExtensionsAction extends Action { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index 32a607b78f119..5b83054fba525 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -167,15 +167,12 @@ export class Renderer implements IPagedRenderer { data.icon.style.visibility = 'inherit'; } - data.root.setAttribute('aria-label', extension.displayName); - removeClass(data.root, 'recommended'); - - const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); - if (extRecommendations[extension.id.toLowerCase()]) { - data.root.setAttribute('aria-label', extension.displayName + '. ' + extRecommendations[extension.id.toLowerCase()].reasonText); - addClass(data.root, 'recommended'); - data.root.title = extRecommendations[extension.id.toLowerCase()].reasonText; - } + this.updateRecommendationStatus(extension, data); + data.extensionDisposables.push(this.extensionTipsService.onRecommendationChange(change => { + if (change.extensionId.toLowerCase() === extension.id.toLowerCase() && change.isRecommended === false) { + this.updateRecommendationStatus(extension, data); + } + })); data.name.textContent = extension.displayName; data.author.textContent = extension.publisherDisplayName; @@ -190,6 +187,20 @@ export class Renderer implements IPagedRenderer { }); } + private updateRecommendationStatus(extension: IExtension, data: ITemplateData) { + const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); + + if (!extRecommendations[extension.id.toLowerCase()]) { + data.root.setAttribute('aria-label', extension.displayName); + data.root.title = ''; + removeClass(data.root, 'recommended'); + } else { + data.root.setAttribute('aria-label', extension.displayName + '. ' + extRecommendations[extension.id]); + data.root.title = extRecommendations[extension.id.toLowerCase()].reasonText; + addClass(data.root, 'recommended'); + } + } + disposeTemplate(data: ITemplateData): void { data.disposables = dispose(data.disposables); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index c230ffaad65f5..4ee01abef1d87 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -32,7 +32,7 @@ import { } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { LocalExtensionType, IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; -import { ExtensionsListView, InstalledExtensionsView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, GroupByServerExtensionsView } from './extensionsViews'; +import { ExtensionsListView, InstalledExtensionsView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, GroupByServerExtensionsView, DefaultRecommendedExtensionsView } from './extensionsViews'; import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -182,7 +182,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio id: 'extensions.recommendedList', name: localize('recommendedExtensions', "Recommended"), container: VIEW_CONTAINER, - ctor: RecommendedExtensionsView, + ctor: DefaultRecommendedExtensionsView, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')), weight: 70, order: 2, diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 24240c479eab7..263423f63679e 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -55,7 +55,7 @@ export class ExtensionsListView extends ViewletPanel { @IExtensionService private extensionService: IExtensionService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, @IEditorService private editorService: IEditorService, - @IExtensionTipsService private tipsService: IExtensionTipsService, + @IExtensionTipsService protected tipsService: IExtensionTipsService, @IModeService private modeService: IModeService, @ITelemetryService private telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService @@ -447,10 +447,10 @@ export class ExtensionsListView extends ViewletPanel { .then(recommendations => { const names = recommendations.filter(name => name.toLowerCase().indexOf(value) > -1); /* __GDPR__ - "extensionWorkspaceRecommendations:open" : { - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ + "extensionWorkspaceRecommendations:open" : { + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ this.telemetryService.publicLog('extensionWorkspaceRecommendations:open', { count: names.length }); if (!names.length) { @@ -645,15 +645,48 @@ export class BuiltInBasicsExtensionsView extends ExtensionsListView { } } +export class DefaultRecommendedExtensionsView extends ExtensionsListView { + + renderBody(container: HTMLElement): void { + super.renderBody(container); + + this.disposables.push(this.tipsService.onRecommendationChange(() => { + this.show(''); + })); + } + + async show(query: string): TPromise> { + return super.show('@recommended:all'); + } +} + export class RecommendedExtensionsView extends ExtensionsListView { + + renderBody(container: HTMLElement): void { + super.renderBody(container); + + this.disposables.push(this.tipsService.onRecommendationChange(() => { + this.show(''); + })); + } + async show(query: string): TPromise> { - return super.show(!query.trim() ? '@recommended:all' : '@recommended'); + return super.show('@recommended'); } } export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { + + renderBody(container: HTMLElement): void { + super.renderBody(container); + + this.disposables.push(this.tipsService.onRecommendationChange(() => { + this.show(''); + })); + } + renderHeader(container: HTMLElement): void { super.renderHeader(container); diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css index 46757df9d56e7..b33e9f8095a1a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css @@ -71,4 +71,29 @@ .hc-black .extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage, .vs-dark .extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage { background: url('manage-inverse.svg') center center no-repeat; +} + +.extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .actions-container { + justify-content: flex-start; +} + +.extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .action-item .action-label.extension-action.ignore { + height: 13px; + width: 8px; + border: none; + outline-offset: 0; + margin-left: 6px; + padding-left: 0; + margin-top: 3px; + background-color: transparent; + color: hsl(0, 66%, 50%); +} + +.hc-black .extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .action-item .action-label.extension-action.ignore, +.vs-dark .extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .action-item .action-label.extension-action.ignore { + color: hsla(0, 66%, 77%, 1); +} + +.extension-editor > .header.recommended > .details > .recommendation > .monaco-action-bar .action-item .action-label.extension-action.ignore:hover { + filter: brightness(.8); } \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css index 42e53acdd0948..f26c5200977bb 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css @@ -20,7 +20,9 @@ font-size: 14px; } -.extension-editor > .header.recommended { +.extension-editor > .header.recommended, +.extension-editor > .header.recommendation-ignored +{ height: 140px; } @@ -128,16 +130,22 @@ padding: 1px 6px; } -.extension-editor > .header.recommended > .details > .recommendation { - display: none; -} -.extension-editor > .header.recommended > .details > .recommendation { - display: block; +.extension-editor > .header.recommended > .details > .recommendation, +.extension-editor > .header.recommendation-ignored > .details > .recommendation { + display: flex; margin-top: 2px; font-size: 13px; } +.extension-editor > .header.recommendation-ignored > .details > .recommendation > .recommendation-text { + font-style: italic; +} + +.extension-editor > .header.recommendation-ignored > .details > .recommendation > .monaco-action-bar { + display: none; +} + .extension-editor > .body { height: calc(100% - 168px); overflow: hidden; diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts index 3f7af9a154cba..ac659cbd8114d 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -5,6 +5,7 @@ 'use strict'; +import * as sinon from 'sinon'; import * as assert from 'assert'; import * as path from 'path'; import * as fs from 'fs'; @@ -36,7 +37,7 @@ import { IPager } from 'vs/base/common/paging'; import { assign } from 'vs/base/common/objects'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IExtensionsWorkbenchService, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; @@ -243,27 +244,30 @@ suite('ExtensionsTipsService Test', () => { }); }); - teardown((done) => { + teardown(done => { (testObject).dispose(); (extensionsWorkbenchService).dispose(); if (parentResource) { extfs.del(parentResource, os.tmpdir(), () => { }, done); + } else { + done(); } }); - function setUpFolderWorkspace(folderName: string, recommendedExtensions: string[]): TPromise { + function setUpFolderWorkspace(folderName: string, recommendedExtensions: string[], ignoredRecommendations: string[] = []): TPromise { const id = uuid.generateUuid(); parentResource = path.join(os.tmpdir(), 'vsctests', id); - return setUpFolder(folderName, parentResource, recommendedExtensions); + return setUpFolder(folderName, parentResource, recommendedExtensions, ignoredRecommendations); } - function setUpFolder(folderName: string, parentDir: string, recommendedExtensions: string[]): TPromise { + function setUpFolder(folderName: string, parentDir: string, recommendedExtensions: string[], ignoredRecommendations: string[] = []): TPromise { const folderDir = path.join(parentDir, folderName); const workspaceSettingsDir = path.join(folderDir, '.vscode'); return mkdirp(workspaceSettingsDir, 493).then(() => { const configPath = path.join(workspaceSettingsDir, 'extensions.json'); fs.writeFileSync(configPath, JSON.stringify({ - 'recommendations': recommendedExtensions + 'recommendations': recommendedExtensions, + 'unwantedRecommendations': ignoredRecommendations, }, null, '\t')); const myWorkspace = testWorkspace(URI.from({ scheme: 'file', path: folderDir })); @@ -276,7 +280,7 @@ suite('ExtensionsTipsService Test', () => { function testNoPromptForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', recommendations).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.promptWorkspaceRecommendationsPromise.then(() => { + return testObject.loadRecommendationsPromise.then(() => { assert.equal(Object.keys(testObject.getAllRecommendationsWithReason()).length, recommendations.length); assert.ok(!prompted); }); @@ -286,7 +290,7 @@ suite('ExtensionsTipsService Test', () => { function testNoPromptOrRecommendationsForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - assert.equal(!testObject.promptWorkspaceRecommendationsPromise, true); + assert.equal(!testObject.loadRecommendationsPromise, true); assert.ok(!prompted); return testObject.getWorkspaceRecommendations().then(() => { @@ -313,7 +317,7 @@ suite('ExtensionsTipsService Test', () => { test('ExtensionTipsService: Prompt for valid workspace recommendations', () => { return setUpFolderWorkspace('myFolder', mockTestData.recommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.promptWorkspaceRecommendationsPromise.then(() => { + return testObject.loadRecommendationsPromise.then(() => { const recommendations = Object.keys(testObject.getAllRecommendationsWithReason()); assert.equal(recommendations.length, mockTestData.validRecommendedExtensions.length); @@ -345,7 +349,7 @@ suite('ExtensionsTipsService Test', () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { showRecommendationsOnlyOnDemand: true }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - return testObject.promptWorkspaceRecommendationsPromise.then(() => { + return testObject.loadRecommendationsPromise.then(() => { assert.equal(Object.keys(testObject.getAllRecommendationsWithReason()).length, 0); assert.ok(!prompted); }); @@ -357,17 +361,152 @@ suite('ExtensionsTipsService Test', () => { return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); }); + test('ExtensionTipsService: No Recommendations of globally ignored recommendations', () => { + const storageGetterStub = (a, _, c) => { + const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "eg2.tslint"]'; + const ignoredRecommendations = '["ms-vscode.csharp", "mockpublisher2.mockextension2"]'; // ignore a stored recommendation and a workspace recommendation. + if (a === 'extensionsAssistant/recommendations') { return storedRecommendations; } + if (a === 'extensionsAssistant/ignored_recommendations') { return ignoredRecommendations; } + return c; + }; + + instantiationService.stub(IStorageService, { + get: storageGetterStub, + getBoolean: (a, _, c) => a === 'extensionsAssistant/workspaceRecommendationsIgnore' || c + }); + + return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { + testObject = instantiationService.createInstance(ExtensionTipsService); + return testObject.loadRecommendationsPromise.then(() => { + const recommendations = testObject.getAllRecommendationsWithReason(); + assert.ok(!recommendations['ms-vscode.csharp']); // stored recommendation that has been globally ignored + assert.ok(recommendations['ms-python.python']); // stored recommendation + assert.ok(recommendations['mockpublisher1.mockextension1']); // workspace recommendation + assert.ok(!recommendations['mockpublisher2.mockextension2']); // workspace recommendation that has been globally ignored + }); + }); + }); + + test('ExtensionTipsService: No Recommendations of workspace ignored recommendations', () => { + const ignoredRecommendations = ['ms-vscode.csharp', 'mockpublisher2.mockextension2']; // ignore a stored recommendation and a workspace recommendation. + const storedRecommendations = '["ms-vscode.csharp", "ms-python.python"]'; + instantiationService.stub(IStorageService, { + get: (a, b, c) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c, + getBoolean: (a, _, c) => a === 'extensionsAssistant/workspaceRecommendationsIgnore' || c + }); + + return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, ignoredRecommendations).then(() => { + testObject = instantiationService.createInstance(ExtensionTipsService); + return testObject.loadRecommendationsPromise.then(() => { + const recommendations = testObject.getAllRecommendationsWithReason(); + assert.ok(!recommendations['ms-vscode.csharp']); // stored recommendation that has been workspace ignored + assert.ok(recommendations['ms-python.python']); // stored recommendation + assert.ok(recommendations['mockpublisher1.mockextension1']); // workspace recommendation + assert.ok(!recommendations['mockpublisher2.mockextension2']); // workspace recommendation that has been workspace ignored + }); + }); + }); + + test('ExtensionTipsService: Able to retrieve collection of all ignored recommendations', () => { + + const storageGetterStub = (a, _, c) => { + const storedRecommendations = '["ms-vscode.csharp", "ms-python.python"]'; + const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. + if (a === 'extensionsAssistant/recommendations') { return storedRecommendations; } + if (a === 'extensionsAssistant/ignored_recommendations') { return globallyIgnoredRecommendations; } + return c; + }; + + const workspaceIgnoredRecommendations = ['ms-vscode.csharp']; // ignore a stored recommendation and a workspace recommendation. + instantiationService.stub(IStorageService, { + get: storageGetterStub, + getBoolean: (a, _, c) => a === 'extensionsAssistant/workspaceRecommendationsIgnore' || c + }); + + return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations).then(() => { + testObject = instantiationService.createInstance(ExtensionTipsService); + return testObject.loadRecommendationsPromise.then(() => { + const recommendations = testObject.getAllIgnoredRecommendations(); + assert.deepStrictEqual(recommendations, + { + global: ['mockpublisher2.mockextension2'], + workspace: ['ms-vscode.csharp'] + }); + }); + }); + }); + + test('ExtensionTipsService: Able to dynamically ignore global recommendations', () => { + const storageGetterStub = (a, _, c) => { + const storedRecommendations = '["ms-vscode.csharp", "ms-python.python"]'; + const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. + if (a === 'extensionsAssistant/recommendations') { return storedRecommendations; } + if (a === 'extensionsAssistant/ignored_recommendations') { return globallyIgnoredRecommendations; } + return c; + }; + + instantiationService.stub(IStorageService, { + get: storageGetterStub, + store: () => { }, + getBoolean: (a, _, c) => a === 'extensionsAssistant/workspaceRecommendationsIgnore' || c + }); + + return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { + testObject = instantiationService.createInstance(ExtensionTipsService); + return testObject.loadRecommendationsPromise.then(() => { + const recommendations = testObject.getAllIgnoredRecommendations(); + assert.deepStrictEqual(recommendations, + { + global: ['mockpublisher2.mockextension2'], + workspace: [] + }); + return testObject.ignoreExtensionRecommendation('mockpublisher1.mockextension1'); + }).then(() => { + const recommendations = testObject.getAllIgnoredRecommendations(); + assert.deepStrictEqual(recommendations, + { + global: ['mockpublisher2.mockextension2', 'mockpublisher1.mockextension1'], + workspace: [] + }); + }); + }); + }); + + test('test global extensions are modified and recommendation change event is fired when an extension is ignored', () => { + const storageSetterTarget = sinon.spy(); + const changeHandlerTarget = sinon.spy(); + const ignoredExtensionId = 'Some.Extension'; + instantiationService.stub(IStorageService, { + get: (a, b, c) => a === 'extensionsAssistant/ignored_recommendations' ? '["ms-vscode.vscode"]' : c, + store: (...args) => { + storageSetterTarget(...args); + } + }); + + return setUpFolderWorkspace('myFolder', []).then(() => { + testObject = instantiationService.createInstance(ExtensionTipsService); + testObject.onRecommendationChange(changeHandlerTarget); + testObject.ignoreExtensionRecommendation(ignoredExtensionId); + + assert.ok(changeHandlerTarget.calledOnce); + assert.ok(changeHandlerTarget.getCall(0).calledWithMatch({ extensionId: 'Some.Extension', isRecommended: false })); + assert.ok(storageSetterTarget.calledWithExactly('extensionsAssistant/ignored_recommendations', `["ms-vscode.vscode","${ignoredExtensionId.toLowerCase()}"]`, StorageScope.GLOBAL)); + }); + }); + test('ExtensionTipsService: Get file based recommendations from storage (old format)', () => { const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "eg2.tslint"]'; instantiationService.stub(IStorageService, { get: (a, b, c) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c }); return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - const recommendations = testObject.getFileBasedRecommendations(); - assert.equal(recommendations.length, 2); - assert.ok(recommendations.indexOf('ms-vscode.csharp') > -1); // stored recommendation that exists in product.extensionTips - assert.ok(recommendations.indexOf('ms-python.python') > -1); // stored recommendation that exists in product.extensionImportantTips - assert.ok(recommendations.indexOf('eg2.tslint') === -1); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips + return testObject.loadRecommendationsPromise.then(() => { + const recommendations = testObject.getFileBasedRecommendations(); + assert.equal(recommendations.length, 2); + assert.ok(recommendations.indexOf('ms-vscode.csharp') > -1); // stored recommendation that exists in product.extensionTips + assert.ok(recommendations.indexOf('ms-python.python') > -1); // stored recommendation that exists in product.extensionImportantTips + assert.ok(recommendations.indexOf('eg2.tslint') === -1); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips + }); }); }); @@ -380,12 +519,14 @@ suite('ExtensionsTipsService Test', () => { return setUpFolderWorkspace('myFolder', []).then(() => { testObject = instantiationService.createInstance(ExtensionTipsService); - const recommendations = testObject.getFileBasedRecommendations(); - assert.equal(recommendations.length, 2); - assert.ok(recommendations.indexOf('ms-vscode.csharp') > -1); // stored recommendation that exists in product.extensionTips - assert.ok(recommendations.indexOf('ms-python.python') > -1); // stored recommendation that exists in product.extensionImportantTips - assert.ok(recommendations.indexOf('eg2.tslint') === -1); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips - assert.ok(recommendations.indexOf('lukehoban.Go') === -1); //stored recommendation that is older than a week + return testObject.loadRecommendationsPromise.then(() => { + const recommendations = testObject.getFileBasedRecommendations(); + assert.equal(recommendations.length, 2); + assert.ok(recommendations.indexOf('ms-vscode.csharp') > -1); // stored recommendation that exists in product.extensionTips + assert.ok(recommendations.indexOf('ms-python.python') > -1); // stored recommendation that exists in product.extensionImportantTips + assert.ok(recommendations.indexOf('eg2.tslint') === -1); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips + assert.ok(recommendations.indexOf('lukehoban.Go') === -1); //stored recommendation that is older than a week + }); }); }); }); From 69d79b07a9538e34d36a6aaf3b73d49673020c89 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 19 Jun 2018 16:13:40 -0700 Subject: [PATCH 080/149] Remove button that shouldnt show up (#52360) --- .../extensions/electron-browser/media/extensionEditor.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css index f26c5200977bb..d5e84bd10d440 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionEditor.css @@ -138,6 +138,10 @@ font-size: 13px; } +.extension-editor > .header > .details > .recommendation { + display: none; +} + .extension-editor > .header.recommendation-ignored > .details > .recommendation > .recommendation-text { font-style: italic; } From fae2d736aa6a0f4879f1c1c3a424476601c70246 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 17:04:05 -0700 Subject: [PATCH 081/149] Adding note on webview persistence --- src/vs/vscode.proposed.d.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index edfb02388f4b1..c43841f11cd73 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -650,6 +650,33 @@ declare module 'vscode' { /** * Restore webview panels that have been persisted when vscode shuts down. + * + * There are two types of webview persistence: + * + * - Persistence within a session. + * - Persistence across sessions (across restarts of VS Code). + * + * A `WebviewPanelSerializer` is only required for the second case: persisting a webview across sessions. + * + * Persistence within a session allows a webview to save its state when it becomes hidden + * and restore its content from this state when it becomes visible again. It is powered entirely + * by the webview content itself. To save off a persisted state, call `acquireVsCodeApi().setState()` with + * any json serializable object. To restore the state again, call `getState()` + * + * ```js + * // Within the webview + * const vscode = acquireVsCodeApi(); + * + * // Get existing state + * const oldState = vscode.getState() || { value: 0 }; + * + * // Update state + * setState({ value: oldState.value + 1 }) + * ``` + * + * A `WebviewPanelSerializer` extends this persistence across restarts of VS Code. When the editor is shutdown, VS Code will save off the state from `setState` of all webviews that have a serializer. When the + * webview first becomes visible after the restart, this state is passed to `deserializeWebviewPanel`. + * The extension can then restore the old `WebviewPanel` from this state. */ interface WebviewPanelSerializer { /** From 7c96862d379b21d3fe603f34f3daf6f60d8c1ca7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Jun 2018 17:24:40 -0700 Subject: [PATCH 082/149] Use workspace edit to create new file on refactor #10659 --- .../src/features/organizeImports.ts | 2 +- .../src/features/quickFix.ts | 2 +- .../src/features/refactor.ts | 45 +++++++++---------- .../src/features/updatePathsOnRename.ts | 2 +- .../src/utils/codeAction.ts | 2 +- .../src/utils/typeConverters.ts | 11 ++++- 6 files changed, 35 insertions(+), 29 deletions(-) diff --git a/extensions/typescript-language-features/src/features/organizeImports.ts b/extensions/typescript-language-features/src/features/organizeImports.ts index 7d8697903a7d8..944fcf7c58582 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/features/organizeImports.ts @@ -39,7 +39,7 @@ class OrganizeImportsCommand implements Command { return false; } - const edits = typeconverts.WorkspaceEdit.fromFromFileCodeEdits(this.client, response.body); + const edits = typeconverts.WorkspaceEdit.fromFileCodeEdits(this.client, response.body); return await vscode.workspace.applyEdit(edits); } } diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 8a9762def39fb..56232596b22fd 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -92,7 +92,7 @@ class ApplyFixAllCodeAction implements Command { return; } - const edit = typeConverters.WorkspaceEdit.fromFromFileCodeEdits(this.client, combinedCodeFixesResponse.body.changes); + const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, combinedCodeFixesResponse.body.changes); await vscode.workspace.applyEdit(edit); if (combinedCodeFixesResponse.command) { diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 7fc60d4b2476b..4aa18755c4ce2 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -4,15 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as fs from 'fs'; - import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { Command, CommandManager } from '../utils/commandManager'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; import FormattingOptionsManager from './fileConfigurationManager'; -import { CommandManager, Command } from '../utils/commandManager'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -import API from '../utils/api'; + class ApplyRefactoringCommand implements Command { public static readonly ID = '_typescript.applyRefactoring'; @@ -35,30 +34,17 @@ class ApplyRefactoringCommand implements Command { action }; const response = await this.client.execute('getEditsForRefactor', args); - if (!response || !response.body || !response.body.edits.length) { + const body = response && response.body; + if (!body || !body.edits.length) { return false; } - for (const edit of response.body.edits) { - try { - await vscode.workspace.openTextDocument(edit.fileName); - } catch { - try { - if (!fs.existsSync(edit.fileName)) { - fs.writeFileSync(edit.fileName, ''); - } - } catch { - // noop - } - } - } - - const edit = typeConverters.WorkspaceEdit.fromFromFileCodeEdits(this.client, response.body.edits); - if (!(await vscode.workspace.applyEdit(edit))) { + const workspaceEdit = await this.toWorkspaceEdit(body); + if (!(await vscode.workspace.applyEdit(workspaceEdit))) { return false; } - const renameLocation = response.body.renameLocation; + const renameLocation = body.renameLocation; if (renameLocation) { await vscode.commands.executeCommand('editor.action.rename', [ document.uri, @@ -67,6 +53,19 @@ class ApplyRefactoringCommand implements Command { } return true; } + + private async toWorkspaceEdit(body: Proto.RefactorEditInfo) { + const workspaceEdit = new vscode.WorkspaceEdit(); + for (const edit of body.edits) { + try { + await vscode.workspace.openTextDocument(edit.fileName); + } catch { + workspaceEdit.createFile(this.client.toResource(edit.fileName)); + } + } + typeConverters.WorkspaceEdit.withFileCodeEdits(workspaceEdit, this.client, body.edits); + return workspaceEdit; + } } class SelectRefactorCommand implements Command { diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index d1b9be9fd3bcd..4e9c0398fea25 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -227,7 +227,7 @@ export class UpdateImportsOnFileRenameHandler { for (const edit of response.body) { edits.push(await this.fixEdit(edit, isDirectoryRename, oldFile, newFile)); } - return typeConverters.WorkspaceEdit.fromFromFileCodeEdits(this.client, edits); + return typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, edits); } private async fixEdit( diff --git a/extensions/typescript-language-features/src/utils/codeAction.ts b/extensions/typescript-language-features/src/utils/codeAction.ts index 984ea6bc9f3f1..d31a40d602d6d 100644 --- a/extensions/typescript-language-features/src/utils/codeAction.ts +++ b/extensions/typescript-language-features/src/utils/codeAction.ts @@ -13,7 +13,7 @@ export function getEditForCodeAction( action: Proto.CodeAction ): WorkspaceEdit | undefined { return action.changes && action.changes.length - ? typeConverters.WorkspaceEdit.fromFromFileCodeEdits(client, action.changes) + ? typeConverters.WorkspaceEdit.fromFileCodeEdits(client, action.changes) : undefined; } diff --git a/extensions/typescript-language-features/src/utils/typeConverters.ts b/extensions/typescript-language-features/src/utils/typeConverters.ts index d65b1468e9d26..8c87338893d94 100644 --- a/extensions/typescript-language-features/src/utils/typeConverters.ts +++ b/extensions/typescript-language-features/src/utils/typeConverters.ts @@ -50,11 +50,18 @@ export namespace TextEdit { } export namespace WorkspaceEdit { - export function fromFromFileCodeEdits( + export function fromFileCodeEdits( + client: ITypeScriptServiceClient, + edits: Iterable + ): vscode.WorkspaceEdit { + return withFileCodeEdits(new vscode.WorkspaceEdit(), client, edits); + + } + export function withFileCodeEdits( + workspaceEdit: vscode.WorkspaceEdit, client: ITypeScriptServiceClient, edits: Iterable ): vscode.WorkspaceEdit { - const workspaceEdit = new vscode.WorkspaceEdit(); for (const edit of edits) { for (const textChange of edit.textChanges) { workspaceEdit.replace(client.toResource(edit.fileName), From 40fb74cf1802c5a77f907170c892d23596588e4a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 19 Jun 2018 17:35:19 -0700 Subject: [PATCH 083/149] Fix #52314 - add aria label for replace toggle state --- src/vs/workbench/parts/search/browser/searchWidget.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index becbaf33d0967..8af51ede3862e 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -241,6 +241,7 @@ export class SearchWidget extends Widget { buttonHoverBackground: null }; this.toggleReplaceButton = this._register(new Button(parent, opts)); + this.toggleReplaceButton.element.setAttribute('aria-expanded', 'false'); this.toggleReplaceButton.icon = 'toggle-replace-button collapse'; // TODO@joh need to dispose this listener eventually this.toggleReplaceButton.onDidClick(() => this.onToggleReplaceButton()); @@ -330,6 +331,7 @@ export class SearchWidget extends Widget { dom.toggleClass(this.replaceContainer, 'disabled'); dom.toggleClass(this.toggleReplaceButton.element, 'collapse'); dom.toggleClass(this.toggleReplaceButton.element, 'expand'); + this.toggleReplaceButton.element.setAttribute('aria-expanded', this.isReplaceShown() ? 'true' : 'false'); this.updateReplaceActiveState(); this._onReplaceToggled.fire(); } From 2b59b800f723847c5083c9b5ac75a67fd012077d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 19 Jun 2018 20:33:51 -0700 Subject: [PATCH 084/149] Settings editor - move TOC back under header --- .../browser/media/settingsEditor2.css | 160 ++++++++---------- .../preferences/browser/settingsEditor2.ts | 14 +- .../parts/preferences/browser/settingsTree.ts | 2 +- 3 files changed, 81 insertions(+), 95 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index ed7f7d4b70816..d8f13862c20fe 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -5,45 +5,40 @@ .settings-editor { padding-top: 11px; - display: flex; - margin: auto; max-width: 1000px; -} - -.settings-editor > .settings-editor-right { - flex: 1; + margin: auto; } /* header styling */ -.settings-editor > .settings-editor-right > .settings-header { +.settings-editor > .settings-header { padding-left: 5px; padding-right: 5px; - max-width: 1000px; box-sizing: border-box; + margin: auto; } -.settings-editor > .settings-editor-right > .settings-header > .settings-preview-header { +.settings-editor > .settings-header > .settings-preview-header { margin-bottom: 5px; } -.settings-editor > .settings-editor-right > .settings-header > .settings-preview-header .settings-preview-label { +.settings-editor > .settings-header > .settings-preview-header .settings-preview-label { opacity: .7; } -.settings-editor > .settings-editor-right > .settings-header > .settings-advanced-customization .open-settings-button, -.settings-editor > .settings-editor-right > .settings-header > .settings-advanced-customization .open-settings-button:hover, -.settings-editor > .settings-editor-right > .settings-header > .settings-advanced-customization .open-settings-button:active { +.settings-editor > .settings-header > .settings-advanced-customization .open-settings-button, +.settings-editor > .settings-header > .settings-advanced-customization .open-settings-button:hover, +.settings-editor > .settings-header > .settings-advanced-customization .open-settings-button:active { padding: 0; text-decoration: underline; display: inline; } -.settings-editor > .settings-editor-right > .settings-header > .settings-advanced-customization { +.settings-editor > .settings-header > .settings-advanced-customization { opacity: .7; margin-top: 10px; } -.settings-editor > .settings-editor-right > .settings-header > .settings-preview-header > .settings-preview-warning { +.settings-editor > .settings-header > .settings-preview-header > .settings-preview-warning { text-align: right; text-transform: uppercase; background: rgba(136, 136, 136, 0.3); @@ -53,140 +48,133 @@ margin-right: 7px; } -.settings-editor > .settings-editor-right > .settings-header > .search-container { +.settings-editor > .settings-header > .search-container { position: relative; } -.settings-editor > .settings-editor-right > .settings-header .search-container > .settings-search-input { +.settings-editor > .settings-header .search-container > .settings-search-input { vertical-align: middle; } -.settings-editor > .settings-editor-right > .settings-header .search-container > .settings-search-input > .monaco-inputbox { +.settings-editor > .settings-header .search-container > .settings-search-input > .monaco-inputbox { height: 30px; width: 100%; } -.settings-editor > .settings-editor-right > .settings-header .search-container > .settings-search-input > .monaco-inputbox .input { +.settings-editor > .settings-header .search-container > .settings-search-input > .monaco-inputbox .input { font-size: 14px; padding-left: 10px; } -.settings-editor > .settings-editor-right > .settings-header > .settings-header-controls { +.settings-editor > .settings-header > .settings-header-controls { margin-top: 2px; height: 30px; display: flex; } -.settings-editor > .settings-editor-right > .settings-header .settings-tabs-widget > .monaco-action-bar .action-item:not(:first-child) .action-label { +.settings-editor > .settings-header .settings-tabs-widget > .monaco-action-bar .action-item:not(:first-child) .action-label { margin-left: 14px; } -.settings-editor > .settings-editor-right > .settings-header .settings-tabs-widget .monaco-action-bar .action-item .dropdown-icon { +.settings-editor > .settings-header .settings-tabs-widget .monaco-action-bar .action-item .dropdown-icon { /** The tab widget container height is shorter than elsewhere, need to tweak this */ padding-top: 3px; } -.settings-editor > .settings-editor-right > .settings-header > .settings-header-controls .settings-header-controls-right { +.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right { margin-left: auto; padding-top: 3px; display: flex; } -.settings-editor > .settings-editor-right > .settings-header > .settings-header-controls .settings-header-controls-right #configured-only-checkbox { +.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right #configured-only-checkbox { flex-shrink: 0; } -.settings-editor > .settings-editor-right > .settings-header > .settings-header-controls .settings-header-controls-right .configured-only-label { +.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .configured-only-label { white-space: nowrap; margin-right: 10px; margin-left: 2px; opacity: 0.7; } -.settings-editor > .settings-editor-right > .settings-body { +.settings-editor > .settings-body { display: flex; + margin: auto; + margin-top: 5px; max-width: 1000px; } -.settings-editor > .settings-editor-right > .settings-body .settings-tree-container .monaco-tree-wrapper { - max-width: 800px; -} - -.settings-editor > .settings-editor-right > .settings-body .settings-tree-container .monaco-scrollable-element .shadow.top-left-corner { - left: calc((100% - 800px)/2); -} - -.settings-editor > .settings-editor-right > .settings-body .settings-tree-container .monaco-scrollable-element .shadow { - left: calc((100% - 794px)/2); - width: 800px; -} - -.settings-editor > .settings-editor-right > .settings-body .settings-tree-container .monaco-tree::before { +.settings-editor > .settings-body .settings-tree-container .monaco-tree::before { outline: none !important; } -.settings-editor > .settings-toc-container { - width: 175px; - margin-top: 114px; +.settings-editor > .settings-body .settings-toc-container { + width: 200px; margin-right: 5px; } -.settings-editor.search-mode > .settings-toc-container .monaco-tree { +.search-mode .settings-toc-container .monaco-tree { display: none; } -.settings-editor > .settings-toc-container .monaco-tree-row .settings-toc-entry { +.settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-entry { overflow: hidden; text-overflow: ellipsis; line-height: 22px; } -.settings-editor > .settings-editor-right > .settings-body .settings-tree-container { +.settings-editor > .settings-body .settings-tree-container { flex: 1; border-spacing: 0; border-collapse: separate; position: relative; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item { - padding-top: 4px; - padding-left: 5px; +.settings-editor > .settings-body > .settings-tree-container .setting-item { + padding-top: 5px; cursor: default; white-space: normal; height: 100%; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item.odd:not(.focused):not(.selected):not(:hover), -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .monaco-tree:not(:focus) .setting-item.focused.odd:not(.selected):not(:hover), -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .monaco-tree:not(.focused) .setting-item.focused.odd:not(.selected):not(:hover) { +.settings-editor > .settings-body > .settings-tree-container .setting-item.odd:not(.focused):not(.selected):not(:hover), +.settings-editor > .settings-body > .settings-tree-container .monaco-tree:not(:focus) .setting-item.focused.odd:not(.selected):not(:hover), +.settings-editor > .settings-body > .settings-tree-container .monaco-tree:not(.focused) .setting-item.focused.odd:not(.selected):not(:hover) { background-color: rgba(130, 130, 130, 0.04); } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-title .setting-item-is-configured-label { +.settings-editor > .settings-body > .settings-tree-container .setting-item > .setting-item-left { + flex: 1; + padding-top: 3px; + padding-bottom: 12px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-title .setting-item-is-configured-label { font-style: italic; opacity: 0.8; margin-right: 7px; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-title .setting-item-overrides { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-title .setting-item-overrides { opacity: 0.5; font-style: italic; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-label { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-label { margin-right: 7px; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-label, -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-category { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-label, +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-category { font-weight: bold; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-category { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-category { opacity: 0.7; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-description { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { opacity: 0.7; margin-top: 3px; overflow: hidden; @@ -195,59 +183,53 @@ height: 18px; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-measure-container.monaco-tree-row { - padding-left: 15px; - opacity: 0; - max-width: 800px; -} - -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-description, -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-description { +.settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-description, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-description { height: initial; white-space: pre-wrap; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value { margin-top: 5px; margin-bottom: 7px; /* Needed when measuring an expanded row */ display: flex; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-number { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-number { min-width: 200px; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-enum, -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-string { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-enum, +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-string { flex: 1; min-width: initial; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-enum > *:first-child { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-enum > *:first-child { width: 100%; min-width: 250px; display: inline-block; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control > .edit-in-settings-button, -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control > .edit-in-settings-button:hover, -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control > .edit-in-settings-button:active { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .edit-in-settings-button, +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .edit-in-settings-button:hover, +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .edit-in-settings-button:active { margin: auto; text-align: left; text-decoration: underline; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control > .edit-in-settings-button + .setting-reset-button.monaco-button { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .edit-in-settings-button + .setting-reset-button.monaco-button { display: none; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .monaco-select-box { +.settings-editor > .settings-body > .settings-tree-container .setting-item .monaco-select-box { width: initial; font: inherit; height: 26px; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-reset-button.monaco-button { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-reset-button.monaco-button { text-align: left; display: inline-block; visibility: hidden; @@ -256,39 +238,43 @@ padding-top: 2px; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item.is-configured .setting-item-value > .setting-reset-button.monaco-button { +.settings-editor > .settings-body > .settings-tree-container .setting-item.is-configured .setting-item-value > .setting-reset-button.monaco-button { visibility: visible; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .all-settings { +.settings-editor > .settings-body > .settings-tree-container .all-settings { display: flex; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .all-settings .all-settings-button { +.settings-editor > .settings-body > .settings-tree-container .all-settings .all-settings-button { margin: auto; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .all-settings .all-settings-button .monaco-button { +.settings-editor > .settings-body > .settings-tree-container .all-settings .all-settings-button .monaco-button { padding-left: 10px; padding-right: 10px; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .settings-group-title-label { +.settings-editor > .settings-body > .settings-tree-container .settings-group-title, +.settings-editor > .settings-body > .settings-tree-container .setting-item { + padding-left: 5px; +} + +.settings-editor > .settings-body > .settings-tree-container .settings-group-title-label { margin: 0px; - padding-left: 5px !important; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .settings-group-level-1 { +.settings-editor > .settings-body > .settings-tree-container .settings-group-level-1 { padding-top: 16px; font-size: 24px; } -.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .settings-group-level-2 { +.settings-editor > .settings-body > .settings-tree-container .settings-group-level-2 { padding-top: 16px; font-size: 20px; } -.settings-editor > .settings-editor-right > .settings-body .settings-feedback-button { +.settings-editor > .settings-body .settings-feedback-button { color: rgb(255, 255, 255); background-color: rgb(14, 99, 156); position: absolute; diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index a9f7a982a6e84..d05e2d6a2bf4f 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -102,11 +102,9 @@ export class SettingsEditor2 extends BaseEditor { createEditor(parent: HTMLElement): void { this.rootElement = DOM.append(parent, $('.settings-editor')); - this.createTOC(this.rootElement); - const settingsEditorRightElement = DOM.append(this.rootElement, $('.settings-editor-right')); - this.createHeader(settingsEditorRightElement); - this.createBody(settingsEditorRightElement); + this.createHeader(this.rootElement); + this.createBody(this.rootElement); } setInput(input: SettingsEditor2Input, options: EditorOptions, token: CancellationToken): Thenable { @@ -217,6 +215,7 @@ export class SettingsEditor2 extends BaseEditor { private createBody(parent: HTMLElement): void { const bodyContainer = DOM.append(parent, $('.settings-body')); + this.createTOC(bodyContainer); this.createSettingsTree(bodyContainer); if (this.environmentService.appQuality !== 'stable') { @@ -237,7 +236,8 @@ export class SettingsEditor2 extends BaseEditor { filter: this.instantiationService.createInstance(SettingsTreeFilter, this.viewState) }, { - showLoading: false + showLoading: false, + twistiePixels: 15 }); this._register(this.tocTree.onDidChangeSelection(e => { @@ -291,12 +291,12 @@ export class SettingsEditor2 extends BaseEditor { this._register(registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const activeBorderColor = theme.getColor(listActiveSelectionBackground); if (activeBorderColor) { - collector.addRule(`.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .monaco-tree:focus .monaco-tree-row.focused {outline: solid 1px ${activeBorderColor}; outline-offset: -1px; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-tree:focus .monaco-tree-row.focused {outline: solid 1px ${activeBorderColor}; outline-offset: -1px; }`); } const inactiveBorderColor = theme.getColor(listInactiveSelectionBackground); if (inactiveBorderColor) { - collector.addRule(`.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .monaco-tree .monaco-tree-row.focused {outline: solid 1px ${inactiveBorderColor}; outline-offset: -1px; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-tree .monaco-tree-row.focused {outline: solid 1px ${inactiveBorderColor}; outline-offset: -1px; }`); } })); diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 5cd8fd536e460..57e2808f647c0 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -41,7 +41,7 @@ export const modifiedItemForeground = registerColor('settings.modifiedItemForegr registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const modifiedItemForegroundColor = theme.getColor(modifiedItemForeground); if (modifiedItemForegroundColor) { - collector.addRule(`.settings-editor > .settings-editor-right > .settings-body > .settings-tree-container .setting-item.is-configured .setting-item-is-configured-label { color: ${modifiedItemForegroundColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.is-configured .setting-item-is-configured-label { color: ${modifiedItemForegroundColor}; }`); } }); From dc15008ccb1132fe39560c99beaf314fdb1f7cd7 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 19 Jun 2018 20:36:39 -0700 Subject: [PATCH 085/149] Settings editor - hide TOC during search --- .../parts/preferences/browser/media/settingsEditor2.css | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index d8f13862c20fe..3ddb397670ea5 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -110,11 +110,11 @@ } .settings-editor > .settings-body .settings-toc-container { - width: 200px; + width: 175px; margin-right: 5px; } -.search-mode .settings-toc-container .monaco-tree { +.search-mode .settings-toc-container { display: none; } @@ -207,8 +207,6 @@ .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-enum > *:first-child { width: 100%; - min-width: 250px; - display: inline-block; } .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .edit-in-settings-button, @@ -255,7 +253,7 @@ padding-right: 10px; } -.settings-editor > .settings-body > .settings-tree-container .settings-group-title, +.settings-editor > .settings-body > .settings-tree-container .group-title, .settings-editor > .settings-body > .settings-tree-container .setting-item { padding-left: 5px; } From 38cbbb781de241a3fd828d89f85eef53c9441102 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Jun 2018 07:52:11 +0200 Subject: [PATCH 086/149] Shorter duration --- .github/new_release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/new_release.yml b/.github/new_release.yml index a17089e852d8e..7482b60b108ea 100644 --- a/.github/new_release.yml +++ b/.github/new_release.yml @@ -1,6 +1,6 @@ { newReleaseLabel: 'new release', newReleaseColor: '006b75', - daysAfterRelease: 7, + daysAfterRelease: 5, perform: true -} \ No newline at end of file +} From fb295d4b9d7831ff591824f20d7b1995093ec3a2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Jun 2018 08:42:46 +0200 Subject: [PATCH 087/149] title :lipstick: --- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index e7d44d9beb20e..7619cce8d02c1 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -83,8 +83,6 @@ export class TitlebarPart extends Part implements ITitleService { this.properties = { isPure: true, isAdmin: false }; this.activeEditorListeners = []; - this.setTitle(this.getWindowTitle()); - this.registerListeners(); } @@ -269,6 +267,8 @@ export class TitlebarPart extends Part implements ITitleService { this.title = $(this.titleContainer).div({ class: 'window-title' }); if (this.pendingTitle) { this.title.text(this.pendingTitle); + } else { + this.setTitle(this.getWindowTitle()); } // Maximize/Restore on doubleclick From d72e9e90ca748cb7c8cf5af6efc6fc434fd89495 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Jun 2018 09:06:58 +0200 Subject: [PATCH 088/149] remove extra center action --- .../parts/editor/editor.contribution.ts | 3 +- .../browser/parts/editor/editorActions.ts | 30 +------------------ 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 51f20e2222bcd..73c180588f7ec 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -37,7 +37,7 @@ import { ShowEditorsInActiveGroupAction, MoveEditorToLastGroupAction, OpenFirstEditorInGroup, MoveGroupUpAction, MoveGroupDownAction, FocusLastGroupAction, SplitEditorLeftAction, SplitEditorRightAction, SplitEditorUpAction, SplitEditorDownAction, MoveEditorToLeftGroupAction, MoveEditorToRightGroupAction, MoveEditorToAboveGroupAction, MoveEditorToBelowGroupAction, CloseAllEditorGroupsAction, JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction, - EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsRightAction, EditorLayoutCenteredAction, NewEditorGroupLeftAction, NewEditorGroupRightAction, + EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction, NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction } from 'vs/workbench/browser/parts/editor/editorActions'; import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands'; @@ -371,7 +371,6 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeRowsA registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoByTwoGridAction, EditorLayoutTwoByTwoGridAction.ID, EditorLayoutTwoByTwoGridAction.LABEL), 'View: Grid Editor Layout (2x2)', category); registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsRightAction, EditorLayoutTwoColumnsRightAction.ID, EditorLayoutTwoColumnsRightAction.LABEL), 'View: Two Columns Right Editor Layout', category); registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutCenteredAction, EditorLayoutCenteredAction.ID, EditorLayoutCenteredAction.LABEL), 'View: Centered Editor Layout', category); // Register Editor Picker Actions including quick navigate support const openNextEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } }; diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 37e225011f0ec..184d01fe29323 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -1565,34 +1565,6 @@ export class EditorLayoutTwoColumnsRightAction extends ExecuteCommandAction { } } -export class EditorLayoutCenteredAction extends Action { - - public static readonly ID = 'workbench.action.editorLayoutCentered'; - public static readonly LABEL = nls.localize('editorLayoutCentered', "Centered Editor Layout"); - - constructor( - id: string, - label: string, - @IPartService private partService: IPartService, - @IEditorGroupsService private editorGroupService: IEditorGroupsService - ) { - super(id, label); - } - - public run(): TPromise { - - // Ensure we can enter centered editor layout even if there are more than 1 groups - if (this.editorGroupService.count > 1) { - mergeAllGroups(this.editorGroupService); - } - - // Center editor layout - this.partService.centerEditorLayout(true); - - return TPromise.as(true); - } -} - export class BaseCreateEditorGroupAction extends Action { constructor( @@ -1665,4 +1637,4 @@ export class NewEditorGroupBelowAction extends BaseCreateEditorGroupAction { ) { super(id, label, GroupDirection.DOWN, editorGroupService); } -} \ No newline at end of file +} From d9483dd7b94193d81c48a0e3327b22cb269bec90 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Jun 2018 09:08:15 +0200 Subject: [PATCH 089/149] fixes #52370 --- src/vs/workbench/parts/debug/browser/debugViewlet.ts | 2 +- .../parts/debug/electron-browser/debug.contribution.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugViewlet.ts b/src/vs/workbench/parts/debug/browser/debugViewlet.ts index 04f9c90f3dbca..e1ea8e7a8f8d4 100644 --- a/src/vs/workbench/parts/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/parts/debug/browser/debugViewlet.ts @@ -226,7 +226,7 @@ export class FocusWatchViewAction extends Action { export class FocusCallStackViewAction extends Action { static readonly ID = 'workbench.debug.action.focusCallStackView'; - static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusCallStackView' }, 'Focus CallStack'); + static LABEL = nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusCallStackView' }, 'Focus Call Stack'); constructor(id: string, label: string, @IViewletService private viewletService: IViewletService diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index b04ba6f0bccbd..a0dd24849eb4e 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -147,7 +147,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(FocusReplAction, Focus registry.registerWorkbenchAction(new SyncActionDescriptor(SelectAndStartAction, SelectAndStartAction.ID, SelectAndStartAction.LABEL), 'Debug: Select and Start Debugging', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusVariablesViewAction, FocusVariablesViewAction.ID, FocusVariablesViewAction.LABEL), 'Debug: Focus Variables', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusWatchViewAction, FocusWatchViewAction.ID, FocusWatchViewAction.LABEL), 'Debug: Focus Watch', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusCallStackViewAction, FocusCallStackViewAction.ID, FocusCallStackViewAction.LABEL), 'Debug: Focus CallStack', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(FocusCallStackViewAction, FocusCallStackViewAction.ID, FocusCallStackViewAction.LABEL), 'Debug: Focus Call Stack', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusBreakpointsViewAction, FocusBreakpointsViewAction.ID, FocusBreakpointsViewAction.LABEL), 'Debug: Focus Breakpoints', debugCategory); From 00863250ae59f26a6dc3b9775291951125b3d3e1 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Jun 2018 09:21:37 +0200 Subject: [PATCH 090/149] Fix possible issue where a first data chunk could contain an entire message --- src/vs/base/parts/ipc/node/ipc.net.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index fee0d49d4d862..0be4208d0e9ac 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -95,10 +95,22 @@ export class Protocol implements IMessagePassingProtocol { } }; - if (firstDataChunk && firstDataChunk.length > 0) { - acceptChunk(firstDataChunk); - } - _socket.on('data', acceptChunk); + const acceptFirstDataChunk = () => { + if (firstDataChunk && firstDataChunk.length > 0) { + let tmp = firstDataChunk; + firstDataChunk = null; + acceptChunk(tmp); + } + }; + + _socket.on('data', (data: Buffer) => { + acceptFirstDataChunk(); + acceptChunk(data); + }); + + _socket.on('end', () => { + acceptFirstDataChunk(); + }); } public send(message: any): void { From 67e1e11845ee43c2a42ca7b5ba961372f96b9c2e Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Jun 2018 09:31:19 +0200 Subject: [PATCH 091/149] remove centered editor layout from layout menu --- src/vs/code/electron-main/menus.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 220377c76035d..5a38604306832 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -676,7 +676,6 @@ export class CodeMenu { const splitEditorRight = this.createMenuItem(nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right"), 'workbench.action.splitEditorRight'); const singleColumnEditorLayout = this.createMenuItem(nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single"), 'workbench.action.editorLayoutSingle'); - const centeredEditorLayout = this.createMenuItem(nls.localize({ key: 'miCenteredEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered"), 'workbench.action.editorLayoutCentered'); const twoColumnsEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns"), 'workbench.action.editorLayoutTwoColumns'); const threeColumnsEditorLayout = this.createMenuItem(nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns"), 'workbench.action.editorLayoutThreeColumns'); const twoRowsEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows"), 'workbench.action.editorLayoutTwoRows'); @@ -694,7 +693,6 @@ export class CodeMenu { splitEditorRight, __separator__(), singleColumnEditorLayout, - centeredEditorLayout, twoColumnsEditorLayout, threeColumnsEditorLayout, twoRowsEditorLayout, From e585e4bf008a50e5b1f4fde71cf6d255e25381bf Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Jun 2018 09:46:24 +0200 Subject: [PATCH 092/149] fixes #52409 --- src/vs/base/browser/ui/centered/centeredViewLayout.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 2a0e06a826f73..a457856aa0b9b 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -76,12 +76,15 @@ export class CenteredViewLayout { } resetView(view: IView): void { - this.view = view; if (this.splitView) { const size = this.splitView.getViewSize(1); this.splitView.removeView(1); - this.splitView.addView(toSplitViewView(this.view, () => this.height), size, 1); + this.splitView.addView(toSplitViewView(view, () => this.height), size, 1); + } else { + this.container.appendChild(view.element); } + + this.view = view; } activate(active: boolean): void { From 90eefbfa87a23c4363cc4eec6661b4cd20bc84fd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Jun 2018 09:47:42 +0200 Subject: [PATCH 093/149] nicer import --- src/vs/workbench/services/files/electron-browser/fileService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index 56cbb5ec49ab0..b79030bd583d3 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -929,7 +929,7 @@ export class FileService implements IFileService { private doMoveItemToTrash(resource: uri): TPromise { const absolutePath = resource.fsPath; - const shell = (require.__$__nodeRequire('electron') as Electron.RendererInterface).shell; // workaround for being able to run tests out of VSCode debugger + const shell = (require('electron') as Electron.RendererInterface).shell; // workaround for being able to run tests out of VSCode debugger const result = shell.moveItemToTrash(absolutePath); if (!result) { return TPromise.wrapError(new Error(isWindows ? nls.localize('binFailed', "Failed to move '{0}' to the recycle bin", paths.basename(absolutePath)) : nls.localize('trashFailed', "Failed to move '{0}' to the trash", paths.basename(absolutePath)))); From 05b5db7a04328b8f404f2ea6f8850a706d089cbc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 10:11:22 +0200 Subject: [PATCH 094/149] manifest ideas about better names in WorkspaceEdit #10659 --- src/vs/vscode.proposed.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c43841f11cd73..7076a28a820db 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -624,6 +624,10 @@ declare module 'vscode' { createFile(uri: Uri): void; deleteFile(uri: Uri): void; renameFile(oldUri: Uri, newUri: Uri): void; + + // replaceText(uri: Uri, range: Range, newText: string): void; + // insertText(uri: Uri, position: Position, newText: string): void; + // deleteText(uri: Uri, range: Range): void; } //#endregion From 6cba561685dd0ebf979558114c7742ef54e76517 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Jun 2018 10:27:20 +0200 Subject: [PATCH 095/149] Writeable active and selected items (#49340, fixes vscode-azure-account#67) --- .../src/singlefolder-tests/quickInput.test.ts | 123 +++++++++++------- .../src/singlefolder-tests/window.test.ts | 12 ++ .../platform/quickinput/common/quickInput.ts | 4 +- src/vs/vscode.proposed.d.ts | 4 +- .../electron-browser/mainThreadQuickOpen.ts | 38 ++++-- src/vs/workbench/api/node/extHost.protocol.ts | 4 + src/vs/workbench/api/node/extHostQuickOpen.ts | 13 ++ .../browser/parts/quickinput/quickInput.ts | 34 ++++- .../parts/quickinput/quickInputList.ts | 40 +++++- 9 files changed, 214 insertions(+), 58 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts index 31fe9e34810e8..7211b8f397cce 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/quickInput.test.ts @@ -9,6 +9,13 @@ import * as assert from 'assert'; import { window, commands } from 'vscode'; import { closeAllEditors } from '../utils'; +interface QuickPickExpected { + events: string[]; + activeItems: string[][]; + selectionItems: string[][]; + acceptedItems: string[][]; +} + suite('window namespace tests', function () { suite('QuickInput tests', function () { @@ -20,59 +27,87 @@ suite('window namespace tests', function () { _done(err); }; - const expectedEvents = ['active', 'active', 'selection', 'accept', 'hide']; - const expectedActiveItems = [['eins'], ['zwei']]; - const expectedSelectionItems = [['zwei']]; + const quickPick = createQuickPick({ + events: ['active', 'active', 'selection', 'accept', 'hide'], + activeItems: [['eins'], ['zwei']], + selectionItems: [['zwei']], + acceptedItems: [['zwei']], + }, done); + quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); + quickPick.show(); - const quickPick = window.createQuickPick(); - quickPick.onDidChangeActive(items => { - try { - assert.equal('active', expectedEvents.shift()); - const expected = expectedActiveItems.shift(); - assert.deepEqual(items.map(item => item.label), expected); - assert.deepEqual(quickPick.activeItems.map(item => item.label), expected); - } catch (err) { - done(err); - } - }); - quickPick.onDidChangeSelection(items => { - try { - assert.equal('selection', expectedEvents.shift()); - const expected = expectedSelectionItems.shift(); - assert.deepEqual(items.map(item => item.label), expected); - assert.deepEqual(quickPick.selectedItems.map(item => item.label), expected); - } catch (err) { - done(err); - } - }); - quickPick.onDidAccept(() => { - try { - assert.equal('accept', expectedEvents.shift()); - const expected = ['zwei']; - assert.deepEqual(quickPick.activeItems.map(item => item.label), expected); - assert.deepEqual(quickPick.selectedItems.map(item => item.label), expected); - quickPick.dispose(); - } catch (err) { - done(err); - } - }); - quickPick.onDidHide(() => { - try { - assert.equal('hide', expectedEvents.shift()); - done(); - } catch (err) { - done(err); - } - }); + (async () => { + await commands.executeCommand('workbench.action.quickOpenSelectNext'); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + })() + .catch(err => done(err)); + }); + test('createQuickPick, focus second', function (_done) { + let done = (err?: any) => { + done = () => {}; + _done(err); + }; + + const quickPick = createQuickPick({ + events: ['active', 'selection', 'accept', 'hide'], + activeItems: [['zwei']], + selectionItems: [['zwei']], + acceptedItems: [['zwei']], + }, done); quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label })); + quickPick.activeItems = [quickPick.items[1]]; quickPick.show(); (async () => { - await commands.executeCommand('workbench.action.quickOpenSelectNext'); await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); })() .catch(err => done(err)); }); }); }); + +function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void) { + const quickPick = window.createQuickPick(); + quickPick.onDidChangeActive(items => { + try { + assert.equal('active', expected.events.shift()); + const expectedItems = expected.activeItems.shift(); + assert.deepEqual(items.map(item => item.label), expectedItems); + assert.deepEqual(quickPick.activeItems.map(item => item.label), expectedItems); + } catch (err) { + done(err); + } + }); + quickPick.onDidChangeSelection(items => { + try { + assert.equal('selection', expected.events.shift()); + const expectedItems = expected.selectionItems.shift(); + assert.deepEqual(items.map(item => item.label), expectedItems); + assert.deepEqual(quickPick.selectedItems.map(item => item.label), expectedItems); + } catch (err) { + done(err); + } + }); + quickPick.onDidAccept(() => { + try { + assert.equal('accept', expected.events.shift()); + const expectedItems = expected.acceptedItems.shift(); + assert.deepEqual(quickPick.activeItems.map(item => item.label), expectedItems); + assert.deepEqual(quickPick.selectedItems.map(item => item.label), expectedItems); + quickPick.dispose(); + } catch (err) { + done(err); + } + }); + quickPick.onDidHide(() => { + try { + assert.equal('hide', expected.events.shift()); + done(); + } catch (err) { + done(err); + } + }); + + return quickPick; +} \ No newline at end of file diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 449cc78536a93..87734062b8a00 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -440,6 +440,18 @@ suite('window namespace tests', () => { assert.deepStrictEqual(await picks, ['eins', 'zwei']); }); + test('showQuickPick, keep selection (Microsoft/vscode-azure-account#67)', async function () { + const picks = window.showQuickPick([ + { label: 'eins' }, + { label: 'zwei', picked: true }, + { label: 'drei', picked: true } + ], { + canPickMany: true + }); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.deepStrictEqual((await picks)!.map(pick => pick.label), ['zwei', 'drei']); + }); + test('showQuickPick, undefined on cancel', function () { const source = new CancellationTokenSource(); const p = window.showQuickPick(['eins', 'zwei', 'drei'], undefined, source.token); diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index ae0f47d225fdf..20da81e28b9d6 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -131,11 +131,11 @@ export interface IQuickPick extends IQuickInput { matchOnDetail: boolean; - readonly activeItems: ReadonlyArray; + activeItems: ReadonlyArray; readonly onDidChangeActive: Event; - readonly selectedItems: ReadonlyArray; + selectedItems: ReadonlyArray; readonly onDidChangeSelection: Event; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7076a28a820db..acd0beaabe07b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -581,11 +581,11 @@ declare module 'vscode' { matchOnDetail: boolean; - readonly activeItems: ReadonlyArray; + activeItems: ReadonlyArray; readonly onDidChangeActive: Event; - readonly selectedItems: ReadonlyArray; + selectedItems: ReadonlyArray; readonly onDidChangeSelection: Event; } diff --git a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts index 1afbe3b936ed1..5afe23566d3b9 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts @@ -12,6 +12,11 @@ import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, Transf import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import URI from 'vs/base/common/uri'; +interface QuickInputSession { + input: IQuickInput; + handlesToItems: Map; +} + @extHostNamedCustomer(MainContext.MainThreadQuickOpen) export class MainThreadQuickOpen implements MainThreadQuickOpenShape { @@ -114,7 +119,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { // ---- QuickInput - private sessions = new Map(); + private sessions = new Map(); $createOrUpdate(params: TransferQuickInput): TPromise { const sessionId = params.id; @@ -140,7 +145,10 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { input.onDidHide(() => { this._proxy.$onDidHide(sessionId); }); - session = input; + session = { + input, + handlesToItems: new Map() + }; } else { const input = this._quickInputService.createInputBox(); input.onDidAccept(() => { @@ -155,22 +163,36 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { input.onDidHide(() => { this._proxy.$onDidHide(sessionId); }); - session = input; + session = { + input, + handlesToItems: new Map() + }; } this.sessions.set(sessionId, session); } + const { input, handlesToItems } = session; for (const param in params) { if (param === 'id' || param === 'type') { continue; } if (param === 'visible') { if (params.visible) { - session.show(); + input.show(); } else { - session.hide(); + input.hide(); } + } else if (param === 'items') { + handlesToItems.clear(); + params[param].forEach(item => { + handlesToItems.set(item.handle, item); + }); + input[param] = params[param]; + } else if (param === 'activeItems' || param === 'selectedItems') { + input[param] = params[param] + .filter(handle => handlesToItems.has(handle)) + .map(handle => handlesToItems.get(handle)); } else if (param === 'buttons') { - session[param] = params.buttons.map(button => { + input[param] = params.buttons.map(button => { if (button.handle === -1) { return this._quickInputService.backButton; } @@ -185,7 +207,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { }; }); } else { - session[param] = params[param]; + input[param] = params[param]; } } return TPromise.as(undefined); @@ -194,7 +216,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { $dispose(sessionId: number): TPromise { const session = this.sessions.get(sessionId); if (session) { - session.dispose(); + session.input.dispose(); this.sessions.delete(sessionId); } return TPromise.as(undefined); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 5dd9e5517bc8e..b037ad5bc3436 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -380,6 +380,10 @@ export interface TransferQuickPick extends BaseTransferQuickInput { items?: TransferQuickPickItems[]; + activeItems?: number[]; + + selectedItems?: number[]; + canSelectMany?: boolean; ignoreFocusOut?: boolean; diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index b7fd0b53aa09c..1673e4b4876cf 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -455,6 +455,7 @@ class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick { private _items: QuickPickItem[] = []; private _handlesToItems = new Map(); + private _itemsToHandles = new Map(); private _canSelectMany = false; private _matchOnDescription = true; private _matchOnDetail = true; @@ -479,8 +480,10 @@ class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick { set items(items: QuickPickItem[]) { this._items = items; this._handlesToItems.clear(); + this._itemsToHandles.clear(); items.forEach((item, i) => { this._handlesToItems.set(i, item); + this._itemsToHandles.set(item, i); }); this.update({ items: items.map((item, i) => ({ @@ -524,12 +527,22 @@ class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick { return this._activeItems; } + set activeItems(activeItems: QuickPickItem[]) { + this._activeItems = activeItems.filter(item => this._itemsToHandles.has(item)); + this.update({ activeItems: this._activeItems.map(item => this._itemsToHandles.get(item)) }); + } + onDidChangeActive = this._onDidChangeActiveEmitter.event; get selectedItems() { return this._selectedItems; } + set selectedItems(selectedItems: QuickPickItem[]) { + this._selectedItems = selectedItems.filter(item => this._itemsToHandles.has(item)); + this.update({ selectedItems: this._selectedItems.map(item => this._itemsToHandles.get(item)) }); + } + onDidChangeSelection = this._onDidChangeSelectionEmitter.event; _fireDidChangeActive(handles: number[]) { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 83529563f38de..5d22a8c62ccec 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -266,8 +266,10 @@ class QuickPick extends QuickInput implements IQuickPick { private _matchOnDescription = true; private _matchOnDetail = true; private _activeItems: IQuickPickItem[] = []; + private activeItemsUpdated = false; private onDidChangeActiveEmitter = new Emitter(); private _selectedItems: IQuickPickItem[] = []; + private selectedItemsUpdated = false; private onDidChangeSelectionEmitter = new Emitter(); private quickNavigate = false; @@ -344,12 +346,24 @@ class QuickPick extends QuickInput implements IQuickPick { return this._activeItems; } + set activeItems(activeItems: IQuickPickItem[]) { + this._activeItems = activeItems; + this.activeItemsUpdated = true; + this.update(); + } + onDidChangeActive = this.onDidChangeActiveEmitter.event; get selectedItems() { return this._selectedItems; } + set selectedItems(selectedItems: IQuickPickItem[]) { + this._selectedItems = selectedItems; + this.selectedItemsUpdated = true; + this.update(); + } + onDidChangeSelection = this.onDidChangeSelectionEmitter.event; show() { @@ -390,6 +404,9 @@ class QuickPick extends QuickInput implements IQuickPick { this.onDidAcceptEmitter.fire(); }), this.ui.list.onDidChangeFocus(focusedItems => { + if (this.activeItemsUpdated) { + return; // Expect another event. + } // Drop initial event. if (!focusedItems.length && !this._activeItems.length) { return; @@ -433,6 +450,7 @@ class QuickPick extends QuickInput implements IQuickPick { this.ui.inputBox.placeholder = (this.placeholder || ''); } if (this.itemsUpdated) { + this.itemsUpdated = false; this.ui.list.setElements(this.items); this.ui.list.filter(this.ui.inputBox.value); this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked(); @@ -440,7 +458,6 @@ class QuickPick extends QuickInput implements IQuickPick { if (!this.canSelectMany) { this.ui.list.focus('First'); } - this.itemsUpdated = false; } if (this.ui.container.classList.contains('show-checkboxes') !== this.canSelectMany) { if (this.canSelectMany) { @@ -449,6 +466,18 @@ class QuickPick extends QuickInput implements IQuickPick { this.ui.list.focus('First'); } } + if (this.activeItemsUpdated) { + this.activeItemsUpdated = false; + this.ui.list.setFocusedElements(this.activeItems); + } + if (this.selectedItemsUpdated) { + this.selectedItemsUpdated = false; + if (this.canSelectMany) { + this.ui.list.setCheckedElements(this.selectedItems); + } else { + this.ui.list.setSelectedElements(this.selectedItems); + } + } this.ui.ignoreFocusOut = this.ignoreFocusOut; this.ui.list.matchOnDescription = this.matchOnDescription; this.ui.list.matchOnDetail = this.matchOnDetail; @@ -878,6 +907,9 @@ export class QuickInputService extends Component implements IQuickInputService { picks.then(items => { input.busy = false; input.items = items; + if (input.canSelectMany) { + input.selectedItems = items.filter(item => item.picked); + } }); input.show(); picks.then(null, err => { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 96551bd6c5cc6..6d5c5416319eb 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -149,6 +149,7 @@ export class QuickInputList { private container: HTMLElement; private list: WorkbenchList; private elements: ListElement[] = []; + private elementsToIndexes = new Map(); matchOnDescription = false; matchOnDetail = false; private _onChangedAllVisibleChecked = new Emitter(); @@ -269,9 +270,14 @@ export class QuickInputList { this.elements = elements.map((item, index) => new ListElement({ index, item, - checked: !!item.picked + checked: false })); this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents()))); + + this.elementsToIndexes = this.elements.reduce((map, element, index) => { + map.set(element.item, index); + return map; + }, new Map()); this.list.splice(0, this.list.length, this.elements); this.list.setFocus([]); } @@ -281,16 +287,44 @@ export class QuickInputList { .map(e => e.item); } + setFocusedElements(items: IQuickPickItem[]) { + this.list.setFocus(items + .filter(item => this.elementsToIndexes.has(item)) + .map(item => this.elementsToIndexes.get(item))); + } + getSelectedElements() { return this.list.getSelectedElements() .map(e => e.item); } + setSelectedElements(items: IQuickPickItem[]) { + this.list.setSelection(items + .filter(item => this.elementsToIndexes.has(item)) + .map(item => this.elementsToIndexes.get(item))); + } + getCheckedElements() { return this.elements.filter(e => e.checked) .map(e => e.item); } + setCheckedElements(items: IQuickPickItem[]) { + try { + this._fireCheckedEvents = false; + const checked = new Set(); + for (const item of items) { + checked.add(item); + } + for (const element of this.elements) { + element.checked = checked.has(element.item); + } + } finally { + this._fireCheckedEvents = true; + this.fireCheckedEvents(); + } + } + set enabled(value: boolean) { this.list.getHTMLElement().style.pointerEvents = value ? null : 'none'; } @@ -368,6 +402,10 @@ export class QuickInputList { return compareEntries(a, b, normalizedSearchValue); }); + this.elementsToIndexes = shownElements.reduce((map, element, index) => { + map.set(element.item, index); + return map; + }, new Map()); this.list.splice(0, this.list.length, shownElements); this.list.setFocus([]); this.list.layout(); From 81f54be0e58ba60c8d36ed71338a07e0138db0a8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Jun 2018 10:45:26 +0200 Subject: [PATCH 096/149] Fix #52342 --- .../extensions/electron-browser/extensionsActions.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index e98faebeb0034..a93ad199ae2a8 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -162,9 +162,6 @@ export class UninstallAction extends Action { private static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling"); - private static readonly UninstallClass = 'extension-action uninstall'; - private static readonly UnInstallingClass = 'extension-action uninstall uninstalling'; - private disposables: IDisposable[] = []; private _extension: IExtension; get extension(): IExtension { return this._extension; } @@ -173,7 +170,7 @@ export class UninstallAction extends Action { constructor( @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService ) { - super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false); + super('extensions.uninstall', UninstallAction.UninstallLabel, null, false); this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); this.update(); @@ -189,13 +186,11 @@ export class UninstallAction extends Action { if (state === ExtensionState.Uninstalling) { this.label = UninstallAction.UninstallingLabel; - this.class = UninstallAction.UnInstallingClass; this.enabled = false; return; } this.label = UninstallAction.UninstallLabel; - this.class = UninstallAction.UninstallClass; const installedExtensions = this.extensionsWorkbenchService.local.filter(e => e.id === this.extension.id); @@ -2123,7 +2118,7 @@ export class InstallVSIXAction extends Action { @INotificationService private notificationService: INotificationService, @IWindowService private windowService: IWindowService ) { - super(id, label, 'extension-action install-vsix', true); + super(id, label, null, true); } run(): TPromise { From 9481b201c5c2d4b2d6d33625858f2fdd51d79a9e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Jun 2018 19:15:46 +1000 Subject: [PATCH 097/149] vscode-xerm@3.5.0-beta13 Fixes #51663 --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index add4abb298022..f2e1c40da1fad 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "vscode-nsfw": "1.0.17", "vscode-ripgrep": "^1.0.1", "vscode-textmate": "^4.0.0-next.2", - "vscode-xterm": "3.5.0-beta12", + "vscode-xterm": "3.5.0-beta13", "yauzl": "^2.9.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index c564a7c1519ea..4f6c93b362736 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6253,9 +6253,9 @@ vscode-textmate@^4.0.0-next.2: fast-plist "^0.1.2" oniguruma "^7.0.0" -vscode-xterm@3.5.0-beta12: - version "3.5.0-beta12" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.5.0-beta12.tgz#853b9bd42ac7bee2ef3deac08d9199e4b7d5dcdb" +vscode-xterm@3.5.0-beta13: + version "3.5.0-beta13" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.5.0-beta13.tgz#8fc24f6d7509e6119d8ec0deb07070e1ed86ddbc" vso-node-api@^6.1.2-preview: version "6.1.2-preview" From c5ef6da6b7d7975bbe447f3a6bc02a418fdd7acf Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Jun 2018 10:18:10 +0200 Subject: [PATCH 098/149] center view: do not reset grid --- .../ui/centered/centeredViewLayout.css | 8 -- .../browser/ui/centered/centeredViewLayout.ts | 77 ++++++++----------- src/vs/base/browser/ui/splitview/splitview.ts | 3 +- .../browser/parts/editor/editorPart.ts | 69 ++++++++++++----- .../parts/editor/media/editorgroupview.css | 5 ++ 5 files changed, 89 insertions(+), 73 deletions(-) delete mode 100644 src/vs/base/browser/ui/centered/centeredViewLayout.css diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.css b/src/vs/base/browser/ui/centered/centeredViewLayout.css deleted file mode 100644 index f7ee1e9083201..0000000000000 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.css +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.centered-view-layout { - height: 100%; -} diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index a457856aa0b9b..b117f3e3d3f52 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -3,24 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./centeredViewLayout'; - import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; import { Event, mapEvent } from 'vs/base/common/event'; import { IView } from 'vs/base/browser/ui/grid/gridview'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { - return { - element: view.element, - maximumSize: view.maximumWidth, - minimumSize: view.minimumWidth, - onDidChange: mapEvent(view.onDidChange, widthAndHeight => widthAndHeight && widthAndHeight.width), - layout: size => view.layout(size, getHeight()) - }; -} - export interface CenteredViewState { leftMarginRatio: number; rightMarginRatio: number; @@ -31,15 +19,34 @@ const GOLDEN_RATIO = { rightMarginRatio: 0.1909 }; +function createEmptyView() { + return { + element: $('.centered-layout-margin'), + layout: () => undefined, + minimumSize: 60, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: Event.None + }; +} + +function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { + return { + element: view.element, + maximumSize: view.maximumWidth, + minimumSize: view.minimumWidth, + onDidChange: mapEvent(view.onDidChange, widthAndHeight => widthAndHeight && widthAndHeight.width), + layout: size => view.layout(size, getHeight()) + }; +} + export class CenteredViewLayout { private splitView: SplitView; - private element: HTMLElement; private width: number = 0; private height: number = 0; private style: ISplitViewStyles; private didLayout = false; - private splitViewDisposable: IDisposable[] = []; + private splitViewDisposables: IDisposable[] = []; constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = GOLDEN_RATIO) { this.container.appendChild(this.view.element); @@ -75,68 +82,48 @@ export class CenteredViewLayout { } } - resetView(view: IView): void { - if (this.splitView) { - const size = this.splitView.getViewSize(1); - this.splitView.removeView(1); - this.splitView.addView(toSplitViewView(view, () => this.height), size, 1); - } else { - this.container.appendChild(view.element); - } - - this.view = view; - } - activate(active: boolean): void { - if (active === !!this.splitView) { + if (active === this.isActive()) { return; } if (active) { - this.element = $('.centered-view-layout'); this.container.removeChild(this.view.element); - this.container.appendChild(this.element); - this.splitView = new SplitView(this.element, { + this.splitView = new SplitView(this.container, { inverseAltBehavior: true, orientation: Orientation.HORIZONTAL, styles: this.style }); - this.splitViewDisposable.push(this.splitView.onDidSashChange(() => { + this.splitViewDisposables.push(this.splitView.onDidSashChange(() => { this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width; this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width; })); - this.splitViewDisposable.push(this.splitView.onDidSashReset(() => { + this.splitViewDisposables.push(this.splitView.onDidSashReset(() => { this.state.leftMarginRatio = GOLDEN_RATIO.leftMarginRatio; this.state.rightMarginRatio = GOLDEN_RATIO.rightMarginRatio; this.resizeMargins(); })); this.splitView.layout(this.width); - - const getEmptyView = () => ({ - element: $('.centered-layout-margin'), - layout: () => undefined, - minimumSize: 60, - maximumSize: Number.POSITIVE_INFINITY, - onDidChange: Event.None - }); - this.splitView.addView(toSplitViewView(this.view, () => this.height), 0); - this.splitView.addView(getEmptyView(), this.state.leftMarginRatio * this.width, 0); - this.splitView.addView(getEmptyView(), this.state.rightMarginRatio * this.width, 2); + this.splitView.addView(createEmptyView(), this.state.leftMarginRatio * this.width, 0); + this.splitView.addView(createEmptyView(), this.state.rightMarginRatio * this.width, 2); } else { - this.splitViewDisposable = dispose(this.splitViewDisposable); + this.container.removeChild(this.splitView.el); + this.splitViewDisposables = dispose(this.splitViewDisposables); this.splitView.dispose(); this.splitView = undefined; - this.container.removeChild(this.element); this.container.appendChild(this.view.element); } } dispose(): void { + this.splitViewDisposables = dispose(this.splitViewDisposables); + if (this.splitView) { this.splitView.dispose(); + this.splitView = undefined; } } } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 9984105f512fe..e06a1aa039146 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -89,7 +89,8 @@ export namespace Sizing { export class SplitView implements IDisposable { readonly orientation: Orientation; - private el: HTMLElement; + // TODO@Joao have the same pattern as grid here + readonly el: HTMLElement; private sashContainer: HTMLElement; private viewContainer: HTMLElement; private size = 0; diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index aed922af57f93..40229727a6d26 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -8,12 +8,12 @@ import 'vs/workbench/browser/parts/editor/editor.contribution'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Part } from 'vs/workbench/browser/part'; -import { Dimension, isAncestor, toggleClass, addClass } from 'vs/base/browser/dom'; +import { Dimension, isAncestor, toggleClass, addClass, $ } from 'vs/base/browser/dom'; import { Event, Emitter, once, Relay, anyEvent } from 'vs/base/common/event'; import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument } from 'vs/workbench/services/group/common/editorGroupsService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, ISerializedNode, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid } from 'vs/base/browser/ui/grid/grid'; +import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, ISerializedNode, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; import { GroupIdentifier, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { values } from 'vs/base/common/map'; import { EDITOR_GROUP_BORDER } from 'vs/workbench/common/theme'; @@ -43,6 +43,48 @@ interface IEditorPartUIState { mostRecentActiveGroups: GroupIdentifier[]; } +class GridWidgetView implements IView { + + readonly element: HTMLElement = $('.grid-view-container'); + + get minimumWidth(): number { return this.gridWidget ? this.gridWidget.minimumWidth : 0; } + get maximumWidth(): number { return this.gridWidget ? this.gridWidget.maximumWidth : Number.POSITIVE_INFINITY; } + get minimumHeight(): number { return this.gridWidget ? this.gridWidget.minimumHeight : 0; } + get maximumHeight(): number { return this.gridWidget ? this.gridWidget.maximumHeight : Number.POSITIVE_INFINITY; } + + private _onDidChange = new Relay<{ width: number; height: number; }>(); + readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event; + + private _gridWidget: Grid; + + get gridWidget(): Grid { + return this._gridWidget; + } + + set gridWidget(grid: Grid) { + this.element.innerHTML = ''; + + if (grid) { + this.element.appendChild(grid.element); + this._onDidChange.input = grid.onDidChange; + } else { + this._onDidChange.input = Event.None; + } + + this._gridWidget = grid; + } + + layout(width: number, height: number): void { + if (this.gridWidget) { + this.gridWidget.layout(width, height); + } + } + + dispose(): void { + this._onDidChange.dispose(); + } +} + export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditorGroupsAccessor { _serviceBrand: any; @@ -91,6 +133,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor private container: HTMLElement; private centeredLayoutWidget: CenteredViewLayout; private gridWidget: SerializableGrid; + private gridWidgetView: GridWidgetView; private _whenRestored: TPromise; private whenRestoredComplete: TValueCallback; @@ -110,6 +153,8 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor ) { super(id, { hasTitle: false }, themeService); + this.gridWidgetView = new GridWidgetView(); + this._partOptions = getEditorPartOptions(this.configurationService.getValue()); this.memento = this.getMemento(this.storageService, Scope.WORKSPACE); this.globalMemento = this.getMemento(this.storageService, Scope.GLOBAL); @@ -720,7 +765,8 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Grid control with center layout this.doCreateGridControl(); - this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.getGridAsView(), this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY])); + + this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY])); // Drop support this._register(this.instantiationService.createInstance(EditorDropTarget, this, this.container)); @@ -728,18 +774,6 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor return this.container; } - private getGridAsView(): IView { - return { - element: this.gridWidget.element, - layout: (width, height) => this.gridWidget.layout(width, height), - minimumWidth: this.gridWidget.minimumWidth, - maximumWidth: this.gridWidget.maximumWidth, - minimumHeight: this.gridWidget.minimumHeight, - maximumHeight: this.gridWidget.minimumHeight, - onDidChange: this.gridWidget.onDidChange - }; - } - centerLayout(active: boolean): void { this.centeredLayoutWidget.activate(active); } @@ -838,12 +872,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor } this.gridWidget = gridWidget; + this.gridWidgetView.gridWidget = gridWidget; if (gridWidget) { - if (this.centeredLayoutWidget) { - this.centeredLayoutWidget.resetView(this.getGridAsView()); - } - this._onDidSizeConstraintsChange.input = gridWidget.onDidChange; } diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index fe09540349336..262514ee87aa1 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -106,4 +106,9 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .editor-container > .editor-instance { height: 100%; +} + +.monaco-workbench > .part.editor > .content .grid-view-container { + width: 100%; + height: 100%; } \ No newline at end of file From c08024879b9f6dcb0984e7606c73ef4c41b156c6 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Jun 2018 10:55:48 +0200 Subject: [PATCH 099/149] centered layout: propagate dimension constraints from view --- .../base/browser/ui/centered/centeredViewLayout.ts | 6 +++--- src/vs/base/browser/ui/splitview/splitview.ts | 13 +++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index b117f3e3d3f52..345a723e5828d 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -32,9 +32,9 @@ function createEmptyView() { function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { return { element: view.element, - maximumSize: view.maximumWidth, - minimumSize: view.minimumWidth, - onDidChange: mapEvent(view.onDidChange, widthAndHeight => widthAndHeight && widthAndHeight.width), + get maximumSize() { return view.maximumWidth; }, + get minimumSize() { return view.minimumWidth; }, + onDidChange: mapEvent(view.onDidChange, e => e && e.width), layout: size => view.layout(size, getHeight()) }; } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index e06a1aa039146..3f593b721e0e5 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -425,8 +425,17 @@ export class SplitView implements IDisposable { size = typeof size === 'number' ? size : item.size; size = clamp(size, item.view.minimumSize, item.view.maximumSize); - item.size = size; - this.relayout(index); + + if (this.inverseAltBehavior && index > 0) { + // In this case, we want the view to grow or shrink both sides equally + // so we just resize the "left" side by half and let `resize` do the clamping magic + this.resize(index - 1, Math.floor((item.size - size) / 2)); + this.distributeEmptySpace(); + this.layoutViews(); + } else { + item.size = size; + this.relayout(index, undefined); + } } resizeView(index: number, size: number): void { From 124ebf6d4762381aaac356abae7ec14e15e95b35 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Jun 2018 10:56:05 +0200 Subject: [PATCH 100/149] splitview: left align all elements --- src/vs/base/browser/ui/splitview/splitview.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/splitview/splitview.css b/src/vs/base/browser/ui/splitview/splitview.css index ed6006b4a1149..6968524236365 100644 --- a/src/vs/base/browser/ui/splitview/splitview.css +++ b/src/vs/base/browser/ui/splitview/splitview.css @@ -37,6 +37,7 @@ .monaco-split-view2 > .split-view-container > .split-view-view { white-space: initial; + flex: none; } .monaco-split-view2.vertical > .split-view-container > .split-view-view { From 19231f69aec930ffdf0c31873019340de6e77785 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Jun 2018 11:41:18 +0200 Subject: [PATCH 101/149] Polish title (#49340) --- .../quickinput/media/dark/arrow-left.svg | 12 ++++ .../parts/quickinput/media/dark/back.svg | 1 - .../quickinput/media/light/arrow-left.svg | 12 ++++ .../parts/quickinput/media/light/back.svg | 1 - .../browser/parts/quickinput/quickInput.css | 26 ++++++-- .../browser/parts/quickinput/quickInput.ts | 62 ++++++++++++------- 6 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 src/vs/workbench/browser/parts/quickinput/media/dark/arrow-left.svg delete mode 100755 src/vs/workbench/browser/parts/quickinput/media/dark/back.svg create mode 100644 src/vs/workbench/browser/parts/quickinput/media/light/arrow-left.svg delete mode 100755 src/vs/workbench/browser/parts/quickinput/media/light/back.svg diff --git a/src/vs/workbench/browser/parts/quickinput/media/dark/arrow-left.svg b/src/vs/workbench/browser/parts/quickinput/media/dark/arrow-left.svg new file mode 100644 index 0000000000000..0d24c218ae1c1 --- /dev/null +++ b/src/vs/workbench/browser/parts/quickinput/media/dark/arrow-left.svg @@ -0,0 +1,12 @@ + + + + arrow-left + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/quickinput/media/dark/back.svg b/src/vs/workbench/browser/parts/quickinput/media/dark/back.svg deleted file mode 100755 index c5c4f472b4d7a..0000000000000 --- a/src/vs/workbench/browser/parts/quickinput/media/dark/back.svg +++ /dev/null @@ -1 +0,0 @@ -CollapseChevronLeft_md_16x \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/quickinput/media/light/arrow-left.svg b/src/vs/workbench/browser/parts/quickinput/media/light/arrow-left.svg new file mode 100644 index 0000000000000..b8362b27458e3 --- /dev/null +++ b/src/vs/workbench/browser/parts/quickinput/media/light/arrow-left.svg @@ -0,0 +1,12 @@ + + + + arrow-left + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/quickinput/media/light/back.svg b/src/vs/workbench/browser/parts/quickinput/media/light/back.svg deleted file mode 100755 index 324ab15b1ec41..0000000000000 --- a/src/vs/workbench/browser/parts/quickinput/media/light/back.svg +++ /dev/null @@ -1 +0,0 @@ -CollapseChevronLeft_md_16x \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.css b/src/vs/workbench/browser/parts/quickinput/quickInput.css index ab5a6f0cfa376..c7301f01e1cbb 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.css +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.css @@ -16,18 +16,36 @@ display: flex; } -.quick-input-title { +.quick-input-left-action-bar { + display: flex; + margin-left: 6px; flex: 1; - padding: 3px 11px; } -.quick-input-action-bar { +.quick-input-left-action-bar.monaco-action-bar .actions-container { + justify-content: flex-start; +} + +.quick-input-left-action-bar .action-label.icon { + margin: 0 5px 0 0; +} + +.quick-input-title { + padding: 3px 0px; + text-align: center; +} + +.quick-input-right-action-bar { display: flex; margin-right: 6px; + flex: 1; } -.quick-input-action-bar .action-label.icon { +.quick-input-right-action-bar .action-label.icon { margin: 0 0 0 5px; +} + +.quick-input-titlebar .monaco-action-bar .action-label.icon { width: 16px; height: 100%; background-position: center; diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 5d22a8c62ccec..1173fa268e862 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -43,13 +43,23 @@ import { IdGenerator } from 'vs/base/common/idGenerator'; const $ = dom.$; +const backButton = { + iconPath: { + dark: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/dark/arrow-left.svg')), + light: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/light/arrow-left.svg')) + }, + tooltip: localize('quickInput.back', "Back"), + handle: -1 // TODO +}; + interface QuickInputUI { container: HTMLElement; + leftActionBar: ActionBar; title: HTMLElement; + rightActionBar: ActionBar; checkAll: HTMLInputElement; inputBox: QuickInputBox; count: CountBadge; - actionBar: ActionBar; message: HTMLElement; progressBar: ProgressBar; list: QuickInputList; @@ -215,8 +225,16 @@ class QuickInput implements IQuickInput { } if (this.buttonsUpdated) { this.buttonsUpdated = false; - this.ui.actionBar.clear(); - this.ui.actionBar.push(this.buttons.map((button, index) => { + this.ui.leftActionBar.clear(); + const leftButtons = this.buttons.filter(button => button === backButton); + this.ui.leftActionBar.push(leftButtons.map((button, index) => { + const action = new Action(`id-${index}`, '', getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button)); + action.tooltip = button.tooltip; + return action; + }), { icon: true, label: false }); + this.ui.rightActionBar.clear(); + const rightButtons = this.buttons.filter(button => button !== backButton); + this.ui.rightActionBar.push(rightButtons.map((button, index) => { const action = new Action(`id-${index}`, '', getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button)); action.tooltip = button.tooltip; return action; @@ -227,7 +245,7 @@ class QuickInput implements IQuickInput { private getTitle() { if (this.title && this.step) { - return `${this.title} ― ${this.getSteps()}`; + return `${this.title} (${this.getSteps()})`; } if (this.title) { return this.title; @@ -240,7 +258,7 @@ class QuickInput implements IQuickInput { private getSteps() { if (this.step && this.totalSteps) { - return localize('quickInput.steps', "{0} of {1}", this.step, this.totalSteps); + return localize('quickInput.steps', "{0}/{1}", this.step, this.totalSteps); } if (this.step) { return String(this.step); @@ -731,11 +749,15 @@ export class QuickInputService extends Component implements IQuickInputService { this.titleBar = dom.append(container, $('.quick-input-titlebar')); + const leftActionBar = new ActionBar(this.titleBar); + leftActionBar.domNode.classList.add('quick-input-left-action-bar'); + this.toUnbind.push(leftActionBar); + const title = dom.append(this.titleBar, $('.quick-input-title')); - const actionBar = new ActionBar(this.titleBar); - actionBar.domNode.classList.add('quick-input-action-bar'); - this.toUnbind.push(actionBar); + const rightActionBar = new ActionBar(this.titleBar); + rightActionBar.domNode.classList.add('quick-input-right-action-bar'); + this.toUnbind.push(rightActionBar); const headerContainer = dom.append(container, $('.quick-input-header')); @@ -835,12 +857,13 @@ export class QuickInputService extends Component implements IQuickInputService { this.ui = { container, + leftActionBar, title, + rightActionBar, checkAll, inputBox, count, message, - actionBar, progressBar, list, onDidAccept: this.onDidAcceptEmitter.event, @@ -970,14 +993,7 @@ export class QuickInputService extends Component implements IQuickInputService { }); } - backButton = { - iconPath: { - dark: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/dark/back.svg')), - light: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/light/back.svg')) - }, - tooltip: localize('quickInput.back', "Back"), - handle: -1 // TODO - }; + backButton = backButton; createQuickPick(): IQuickPick { this.create(); @@ -999,14 +1015,15 @@ export class QuickInputService extends Component implements IQuickInputService { } this.setEnabled(true); + this.ui.leftActionBar.clear(); this.ui.title.textContent = ''; + this.ui.rightActionBar.clear(); this.ui.checkAll.checked = false; // this.ui.inputBox.value = ''; Avoid triggering an event. this.ui.inputBox.placeholder = ''; this.ui.inputBox.password = false; this.ui.inputBox.showDecoration(Severity.Ignore); this.ui.count.setCount(0); - this.ui.actionBar.clear(); this.ui.message.textContent = ''; this.ui.progressBar.stop(); this.ui.list.setElements([]); @@ -1036,11 +1053,14 @@ export class QuickInputService extends Component implements IQuickInputService { private setEnabled(enabled: boolean) { if (enabled !== this.enabled) { this.enabled = enabled; - this.ui.checkAll.disabled = !enabled; - this.ui.inputBox.enabled = enabled; - for (const item of this.ui.actionBar.items) { + for (const item of this.ui.leftActionBar.items) { + (item as ActionItem).getAction().enabled = enabled; + } + for (const item of this.ui.rightActionBar.items) { (item as ActionItem).getAction().enabled = enabled; } + this.ui.checkAll.disabled = !enabled; + this.ui.inputBox.enabled = enabled; this.ok.enabled = enabled; this.ui.list.enabled = enabled; } From b0be57d6926baee4a6e730f3a7445e736961bf18 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Jun 2018 12:10:48 +0200 Subject: [PATCH 102/149] Fix ignoreFocusOut for showInputBox (fixes #52361) --- src/vs/workbench/browser/parts/quickinput/quickInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 1173fa268e862..b5fca918a6659 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -240,6 +240,7 @@ class QuickInput implements IQuickInput { return action; }), { icon: true, label: false }); } + this.ui.ignoreFocusOut = this.ignoreFocusOut; this.ui.setEnabled(this.enabled); } @@ -496,7 +497,6 @@ class QuickPick extends QuickInput implements IQuickPick { this.ui.list.setSelectedElements(this.selectedItems); } } - this.ui.ignoreFocusOut = this.ignoreFocusOut; this.ui.list.matchOnDescription = this.matchOnDescription; this.ui.list.matchOnDetail = this.matchOnDetail; this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, count: true, ok: true, list: true } : { title: !!this.title || !!this.step, inputBox: true, list: true }); From 5d0ec1ad78646e285e743274730a36cb9495a138 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Jun 2018 12:37:15 +0200 Subject: [PATCH 103/149] Fix infinite progress (#49340) --- src/vs/workbench/browser/parts/quickinput/quickInput.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index b5fca918a6659..be1a419ed6045 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -215,7 +215,9 @@ class QuickInput implements IQuickInput { if (this.busy && !this.busyDelay) { this.busyDelay = TPromise.timeout(800); this.busyDelay.then(() => { - this.ui.progressBar.infinite(); + if (this.visible) { + this.ui.progressBar.infinite(); + } }, () => { /* ignore */ }); } if (!this.busy && this.busyDelay) { From 8b6281e3626543ea457b9a429cd4e4068e4063dc Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Jun 2018 12:40:44 +0200 Subject: [PATCH 104/149] Ensure unique decoration keys --- .../workbench/api/electron-browser/mainThreadEditors.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts index 737c5de4d2507..8636585f8c237 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts @@ -30,6 +30,9 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; export class MainThreadTextEditors implements MainThreadTextEditorsShape { + private static INSTANCE_COUNT: number = 0; + + private _instanceId: string; private _proxy: ExtHostEditorsShape; private _documentsAndEditors: MainThreadDocumentsAndEditors; private _toDispose: IDisposable[]; @@ -45,6 +48,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { @IEditorService private readonly _editorService: IEditorService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService ) { + this._instanceId = String(++MainThreadTextEditors.INSTANCE_COUNT); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditors); this._documentsAndEditors = documentsAndEditors; this._toDispose = []; @@ -167,6 +171,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } $trySetDecorations(id: string, key: string, ranges: IDecorationOptions[]): TPromise { + key = `${this._instanceId}-${key}`; if (!this._documentsAndEditors.getEditor(id)) { return TPromise.wrapError(disposed(`TextEditor(${id})`)); } @@ -175,6 +180,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } $trySetDecorationsFast(id: string, key: string, ranges: number[]): TPromise { + key = `${this._instanceId}-${key}`; if (!this._documentsAndEditors.getEditor(id)) { return TPromise.wrapError(disposed(`TextEditor(${id})`)); } @@ -218,11 +224,13 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } $registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void { + key = `${this._instanceId}-${key}`; this._registeredDecorationTypes[key] = true; this._codeEditorService.registerDecorationType(key, options); } $removeTextEditorDecorationType(key: string): void { + key = `${this._instanceId}-${key}`; delete this._registeredDecorationTypes[key]; this._codeEditorService.removeDecorationType(key); } From c570b7364e2a26732a51feb9d1b4dceda590467d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Jun 2018 12:45:17 +0200 Subject: [PATCH 105/149] Fix tests --- .../test/electron-browser/extensionsActions.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 16d79e6353f95..f6df23bd3a889 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -192,7 +192,7 @@ suite('ExtensionsActions Test', () => { uninstallEvent.fire(local.identifier); assert.ok(!testObject.enabled); assert.equal('Uninstalling', testObject.label); - assert.equal('extension-action uninstall uninstalling', testObject.class); + assert.equal(null, testObject.class); }); }); @@ -206,7 +206,7 @@ suite('ExtensionsActions Test', () => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); assert.equal('Uninstall', testObject.label); - assert.equal('extension-action uninstall', testObject.class); + assert.equal(null, testObject.class); }); }); @@ -220,7 +220,7 @@ suite('ExtensionsActions Test', () => { testObject.extension = extensions[0]; assert.ok(!testObject.enabled); assert.equal('Uninstall', testObject.label); - assert.equal('extension-action uninstall', testObject.class); + assert.equal(null, testObject.class); }); }); @@ -238,7 +238,7 @@ suite('ExtensionsActions Test', () => { assert.ok(testObject.enabled); assert.equal('Uninstall', testObject.label); - assert.equal('extension-action uninstall', testObject.class); + assert.equal(null, testObject.class); }); }); @@ -287,7 +287,7 @@ suite('ExtensionsActions Test', () => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); assert.equal('Uninstall', testObject.label); - assert.equal('extension-action uninstall', testObject.class); + assert.equal(null, testObject.class); }); }); @@ -318,7 +318,7 @@ suite('ExtensionsActions Test', () => { uninstallEvent.fire(local.identifier); assert.ok(!testObject.enabled); assert.equal('Uninstalling', testObject.label); - assert.equal('extension-action uninstall uninstalling', testObject.class); + assert.equal(null, testObject.class); }); }); From 97f48d7bba360d151e5ad5e861f72196d26bdfcb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 12:25:09 +0200 Subject: [PATCH 106/149] rename/create should be able to define override behaviour #10659 --- src/vs/editor/common/modes.ts | 1 + src/vs/monaco.d.ts | 3 ++ src/vs/vscode.proposed.d.ts | 4 +-- src/vs/workbench/api/node/extHost.protocol.ts | 1 + .../workbench/api/node/extHostApiCommands.ts | 4 +-- .../api/node/extHostLanguageFeatures.ts | 6 ++-- .../api/node/extHostTypeConverters.ts | 9 +++--- src/vs/workbench/api/node/extHostTypes.ts | 32 +++++++++++++------ .../electron-browser/bulkEditService.ts | 4 +-- .../electron-browser/api/extHostTypes.test.ts | 4 +-- .../api/mainThreadEditors.test.ts | 6 ++-- 11 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 0f14cfc72265c..cc2d937768b63 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -899,6 +899,7 @@ export function isResourceTextEdit(thing: any): thing is ResourceTextEdit { export interface ResourceFileEdit { oldUri: URI; newUri: URI; + options: { override: boolean }; } export interface ResourceTextEdit { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 05aad76b9ef9a..a97334410aadd 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5100,6 +5100,9 @@ declare namespace monaco.languages { export interface ResourceFileEdit { oldUri: Uri; newUri: Uri; + options: { + override: boolean; + }; } export interface ResourceTextEdit { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index acd0beaabe07b..8b93e1b86f478 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -621,9 +621,9 @@ declare module 'vscode' { //#region joh: https://github.com/Microsoft/vscode/issues/10659 export interface WorkspaceEdit { - createFile(uri: Uri): void; + createFile(uri: Uri, options?: { override?: boolean }): void; deleteFile(uri: Uri): void; - renameFile(oldUri: Uri, newUri: Uri): void; + renameFile(oldUri: Uri, newUri: Uri, options?: { override?: boolean }): void; // replaceText(uri: Uri, range: Range, newText: string): void; // insertText(uri: Uri, position: Position, newText: string): void; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index b037ad5bc3436..be70e46cee364 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -766,6 +766,7 @@ export interface WorkspaceSymbolsDto extends IdObject { export interface ResourceFileEditDto { oldUri: UriComponents; newUri: UriComponents; + options: { override?: boolean }; } export interface ResourceTextEditDto { diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 3ff0a6186c004..d3467d37632d9 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -10,7 +10,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import * as vscode from 'vscode'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import * as types from 'vs/workbench/api/node/extHostTypes'; -import { IRawColorInfo } from 'vs/workbench/api/node/extHost.protocol'; +import { IRawColorInfo, WorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol'; import { ISingleEditOperation } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import * as search from 'vs/workbench/parts/search/common/search'; @@ -344,7 +344,7 @@ export class ExtHostApiCommands { position: position && typeConverters.Position.from(position), newName }; - return this._commands.executeCommand('_executeDocumentRenameProvider', args).then(value => { + return this._commands.executeCommand('_executeDocumentRenameProvider', args).then(value => { if (!value) { return undefined; } diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 49120b1c2911c..83f75db9b2557 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -495,18 +495,18 @@ class RenameAdapter { return typeConvert.WorkspaceEdit.from(value); }, err => { if (typeof err === 'string') { - return { + return { edits: undefined, rejectReason: err }; } else if (err instanceof Error && typeof err.message === 'string') { - return { + return { edits: undefined, rejectReason: err.message }; } else { // generic error - return TPromise.wrapError(err); + return TPromise.wrapError(err); } }); } diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 9aed1474620fb..2c5a3cc0babbe 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -271,7 +271,7 @@ export const TextEdit = { export namespace WorkspaceEdit { export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors): WorkspaceEditDto { - const result: modes.WorkspaceEdit = { + const result: WorkspaceEditDto = { edits: [] }; for (const entry of (value as types.WorkspaceEdit).allEntries()) { @@ -279,10 +279,10 @@ export namespace WorkspaceEdit { if (Array.isArray(uriOrEdits)) { // text edits let doc = documents ? documents.getDocument(uri.toString()) : undefined; - result.edits.push({ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) }); + result.edits.push({ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) }); } else { // resource edits - result.edits.push({ oldUri: uri, newUri: uriOrEdits }); + result.edits.push({ oldUri: uri, newUri: uriOrEdits, options: entry[2] }); } } return result; @@ -299,7 +299,8 @@ export namespace WorkspaceEdit { } else { result.renameFile( URI.revive((edit).oldUri), - URI.revive((edit).newUri) + URI.revive((edit).newUri), + (edit).options ); } } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 39658d10c72ae..66681d42b5a7a 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -495,21 +495,33 @@ export class TextEdit { } +export interface IFileOperation { + _type: 1; + from: URI; + to: URI; + options?: { override?: boolean }; +} + +export interface IFileTextEdit { + _type: 2; + uri: URI; + edit: TextEdit; +} export class WorkspaceEdit implements vscode.WorkspaceEdit { - private _edits = new Array<{ _type: 1, from: URI, to: URI } | { _type: 2, uri: URI, edit: TextEdit }>(); + private _edits = new Array(); - createFile(uri: vscode.Uri): void { - this.renameFile(undefined, uri); + renameFile(from: vscode.Uri, to: vscode.Uri, options?: { override?: boolean }): void { + this._edits.push({ _type: 1, from, to, options }); } - deleteFile(uri: vscode.Uri): void { - this.renameFile(uri, undefined); + createFile(uri: vscode.Uri, options?: { override?: boolean }): void { + this.renameFile(undefined, uri, options); } - renameFile(from: vscode.Uri, to: vscode.Uri): void { - this._edits.push({ _type: 1, from, to }); + deleteFile(uri: vscode.Uri): void { + this.renameFile(uri, undefined); } replace(uri: URI, range: Range, newText: string): void { @@ -581,11 +593,11 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { return values(textEdits); } - allEntries(): ([URI, TextEdit[]] | [URI, URI])[] { - let res: ([URI, TextEdit[]] | [URI, URI])[] = []; + allEntries(): ([URI, TextEdit[]] | [URI, URI, { override?: boolean }])[] { + let res: ([URI, TextEdit[]] | [URI, URI, { override?: boolean }])[] = []; for (let edit of this._edits) { if (edit._type === 1) { - res.push([edit.from, edit.to]); + res.push([edit.from, edit.to, edit.options]); } else { res.push([edit.uri, [edit.edit]]); } diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index 1d989c07002ce..3b661348e19a8 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -336,11 +336,11 @@ export class BulkEdit { progress.report(undefined); if (edit.newUri && edit.oldUri) { - await this._textFileService.move(edit.oldUri, edit.newUri, false); + await this._textFileService.move(edit.oldUri, edit.newUri, edit.options && edit.options.override); } else if (!edit.newUri && edit.oldUri) { await this._textFileService.delete(edit.oldUri, true); } else if (edit.newUri && !edit.oldUri) { - await this._fileService.createFile(edit.newUri, undefined, { overwrite: false }); + await this._fileService.createFile(edit.newUri, undefined, { overwrite: edit.options && edit.options.override }); } } } diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index 11e5187b6e6f1..add4566a3f62e 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -386,12 +386,12 @@ suite('ExtHostTypes', function () { const all = edit.allEntries(); assert.equal(all.length, 4); - function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI]): thing is [URI, URI] { + function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI, { override?: boolean }]): thing is [URI, URI, { override?: boolean }] { const [f, s] = thing; return URI.isUri(f) && URI.isUri(s); } - function isTextChange(thing: [URI, types.TextEdit[]] | [URI, URI]): thing is [URI, types.TextEdit[]] { + function isTextChange(thing: [URI, types.TextEdit[]] | [URI, URI, { override?: boolean }]): thing is [URI, types.TextEdit[]] { const [f, s] = thing; return URI.isUri(f) && Array.isArray(s); } diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index 7277d63ccc5b2..9e79075d377e7 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -135,9 +135,9 @@ suite('MainThreadEditors', () => { test(`applyWorkspaceEdit with only resource edit`, () => { return editors.$tryApplyWorkspaceEdit({ edits: [ - { oldUri: resource, newUri: resource }, - { oldUri: undefined, newUri: resource }, - { oldUri: resource, newUri: undefined } + { oldUri: resource, newUri: resource, options: undefined }, + { oldUri: undefined, newUri: resource, options: undefined }, + { oldUri: resource, newUri: undefined, options: undefined } ] }).then((result) => { assert.equal(result, true); From bac2a593221e7c1141575cc4d481fe27a07d9c89 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 12:46:47 +0200 Subject: [PATCH 107/149] add partial test #10659 --- .../src/singlefolder-tests/workspace.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 407f8dbf2e545..ae499b1e94b96 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -666,4 +666,20 @@ suite('workspace-namespace', () => { let doc = await vscode.workspace.openTextDocument(newUri); assert.equal(doc.getText(), 'Hello'); }); + + test('WorkspaceEdit: create & override', async function () { + + let docUri = await createRandomFile('before'); + + let we = new vscode.WorkspaceEdit(); + we.createFile(docUri); + assert.ok(!await vscode.workspace.applyEdit(we)); + assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), 'before'); + + we = new vscode.WorkspaceEdit(); + we.createFile(docUri, { override: true }); + assert.ok(await vscode.workspace.applyEdit(we)); + // todo@ben + // assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), ''); + }); }); From 3db3905ee1690d5e9f7f5c5295bc01f713868bc4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Jun 2018 12:49:57 +0200 Subject: [PATCH 108/149] Revert "Fix #52342" This reverts commit 81f54be0e58ba60c8d36ed71338a07e0138db0a8. --- .../extensions/electron-browser/extensionsActions.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index a93ad199ae2a8..e98faebeb0034 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -162,6 +162,9 @@ export class UninstallAction extends Action { private static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling"); + private static readonly UninstallClass = 'extension-action uninstall'; + private static readonly UnInstallingClass = 'extension-action uninstall uninstalling'; + private disposables: IDisposable[] = []; private _extension: IExtension; get extension(): IExtension { return this._extension; } @@ -170,7 +173,7 @@ export class UninstallAction extends Action { constructor( @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService ) { - super('extensions.uninstall', UninstallAction.UninstallLabel, null, false); + super('extensions.uninstall', UninstallAction.UninstallLabel, UninstallAction.UninstallClass, false); this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); this.update(); @@ -186,11 +189,13 @@ export class UninstallAction extends Action { if (state === ExtensionState.Uninstalling) { this.label = UninstallAction.UninstallingLabel; + this.class = UninstallAction.UnInstallingClass; this.enabled = false; return; } this.label = UninstallAction.UninstallLabel; + this.class = UninstallAction.UninstallClass; const installedExtensions = this.extensionsWorkbenchService.local.filter(e => e.id === this.extension.id); @@ -2118,7 +2123,7 @@ export class InstallVSIXAction extends Action { @INotificationService private notificationService: INotificationService, @IWindowService private windowService: IWindowService ) { - super(id, label, null, true); + super(id, label, 'extension-action install-vsix', true); } run(): TPromise { From 38b11bf5d56eca0041e50e82cc9bfb2b13b75d33 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Jun 2018 12:56:09 +0200 Subject: [PATCH 109/149] Revert "Fix tests" This reverts commit c570b7364e2a26732a51feb9d1b4dceda590467d. --- .../test/electron-browser/extensionsActions.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index f6df23bd3a889..16d79e6353f95 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -192,7 +192,7 @@ suite('ExtensionsActions Test', () => { uninstallEvent.fire(local.identifier); assert.ok(!testObject.enabled); assert.equal('Uninstalling', testObject.label); - assert.equal(null, testObject.class); + assert.equal('extension-action uninstall uninstalling', testObject.class); }); }); @@ -206,7 +206,7 @@ suite('ExtensionsActions Test', () => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); assert.equal('Uninstall', testObject.label); - assert.equal(null, testObject.class); + assert.equal('extension-action uninstall', testObject.class); }); }); @@ -220,7 +220,7 @@ suite('ExtensionsActions Test', () => { testObject.extension = extensions[0]; assert.ok(!testObject.enabled); assert.equal('Uninstall', testObject.label); - assert.equal(null, testObject.class); + assert.equal('extension-action uninstall', testObject.class); }); }); @@ -238,7 +238,7 @@ suite('ExtensionsActions Test', () => { assert.ok(testObject.enabled); assert.equal('Uninstall', testObject.label); - assert.equal(null, testObject.class); + assert.equal('extension-action uninstall', testObject.class); }); }); @@ -287,7 +287,7 @@ suite('ExtensionsActions Test', () => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); assert.equal('Uninstall', testObject.label); - assert.equal(null, testObject.class); + assert.equal('extension-action uninstall', testObject.class); }); }); @@ -318,7 +318,7 @@ suite('ExtensionsActions Test', () => { uninstallEvent.fire(local.identifier); assert.ok(!testObject.enabled); assert.equal('Uninstalling', testObject.label); - assert.equal(null, testObject.class); + assert.equal('extension-action uninstall uninstalling', testObject.class); }); }); From 738fa55a52519d00d93a5b4744acbb69e4657630 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Jun 2018 12:59:02 +0200 Subject: [PATCH 110/149] #52342 Fix classes contributed to themes --- .../electron-browser/extensionsActions.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index e98faebeb0034..45aa343cc2139 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -2249,41 +2249,49 @@ export const extensionButtonProminentHoverBackground = registerColor('extensionB registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const foregroundColor = theme.getColor(foreground); if (foregroundColor) { - collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.built-in-status { border-color: ${foregroundColor}; }`); } const buttonBackgroundColor = theme.getColor(buttonBackground); if (buttonBackgroundColor) { - collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { background-color: ${buttonBackgroundColor}; }`); } const buttonForegroundColor = theme.getColor(buttonForeground); if (buttonForegroundColor) { - collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { color: ${buttonForegroundColor}; }`); } const buttonHoverBackgroundColor = theme.getColor(buttonHoverBackground); if (buttonHoverBackgroundColor) { - collector.addRule(`.monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item:hover .action-label.extension-action { background-color: ${buttonHoverBackgroundColor}; }`); } const contrastBorderColor = theme.getColor(contrastBorder); if (contrastBorderColor) { - collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action { border: 1px solid ${contrastBorderColor}; }`); } const extensionButtonProminentBackgroundColor = theme.getColor(extensionButtonProminentBackground); if (extensionButtonProminentBackground) { - collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); } const extensionButtonProminentForegroundColor = theme.getColor(extensionButtonProminentForeground); if (extensionButtonProminentForeground) { - collector.addRule(`.monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); } const extensionButtonProminentHoverBackgroundColor = theme.getColor(extensionButtonProminentHoverBackground); if (extensionButtonProminentHoverBackground) { - collector.addRule(`.monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item:hover .action-label.extension-action.prominent { background-color: ${extensionButtonProminentHoverBackgroundColor}; }`); } }); From f0ec28b49958f48707ff1be5247e3e37e0f14641 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 13:16:42 +0200 Subject: [PATCH 111/149] override -> overwrite, #10659 --- .../src/singlefolder-tests/workspace.test.ts | 2 +- src/vs/editor/common/modes.ts | 2 +- src/vs/monaco.d.ts | 2 +- src/vs/vscode.proposed.d.ts | 4 ++-- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- src/vs/workbench/api/node/extHostTypes.ts | 10 +++++----- .../bulkEdit/electron-browser/bulkEditService.ts | 7 ++++--- .../test/electron-browser/api/extHostTypes.test.ts | 4 ++-- 8 files changed, 17 insertions(+), 16 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index ae499b1e94b96..03b5f98cad5e8 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -677,7 +677,7 @@ suite('workspace-namespace', () => { assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), 'before'); we = new vscode.WorkspaceEdit(); - we.createFile(docUri, { override: true }); + we.createFile(docUri, { overwrite: true }); assert.ok(await vscode.workspace.applyEdit(we)); // todo@ben // assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), ''); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index cc2d937768b63..6c79dab73e530 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -899,7 +899,7 @@ export function isResourceTextEdit(thing: any): thing is ResourceTextEdit { export interface ResourceFileEdit { oldUri: URI; newUri: URI; - options: { override: boolean }; + options: { overwrite: boolean }; } export interface ResourceTextEdit { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index a97334410aadd..37a856ec4f338 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5101,7 +5101,7 @@ declare namespace monaco.languages { oldUri: Uri; newUri: Uri; options: { - override: boolean; + overwrite: boolean; }; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 8b93e1b86f478..840fd9027cb7b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -621,9 +621,9 @@ declare module 'vscode' { //#region joh: https://github.com/Microsoft/vscode/issues/10659 export interface WorkspaceEdit { - createFile(uri: Uri, options?: { override?: boolean }): void; + createFile(uri: Uri, options?: { overwrite?: boolean }): void; deleteFile(uri: Uri): void; - renameFile(oldUri: Uri, newUri: Uri, options?: { override?: boolean }): void; + renameFile(oldUri: Uri, newUri: Uri, options?: { overwrite?: boolean }): void; // replaceText(uri: Uri, range: Range, newText: string): void; // insertText(uri: Uri, position: Position, newText: string): void; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index be70e46cee364..7407b8068acd2 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -766,7 +766,7 @@ export interface WorkspaceSymbolsDto extends IdObject { export interface ResourceFileEditDto { oldUri: UriComponents; newUri: UriComponents; - options: { override?: boolean }; + options: { overwrite?: boolean }; } export interface ResourceTextEditDto { diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 66681d42b5a7a..2a629d5591871 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -499,7 +499,7 @@ export interface IFileOperation { _type: 1; from: URI; to: URI; - options?: { override?: boolean }; + options?: { overwrite?: boolean }; } export interface IFileTextEdit { @@ -512,11 +512,11 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { private _edits = new Array(); - renameFile(from: vscode.Uri, to: vscode.Uri, options?: { override?: boolean }): void { + renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean }): void { this._edits.push({ _type: 1, from, to, options }); } - createFile(uri: vscode.Uri, options?: { override?: boolean }): void { + createFile(uri: vscode.Uri, options?: { overwrite?: boolean }): void { this.renameFile(undefined, uri, options); } @@ -593,8 +593,8 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { return values(textEdits); } - allEntries(): ([URI, TextEdit[]] | [URI, URI, { override?: boolean }])[] { - let res: ([URI, TextEdit[]] | [URI, URI, { override?: boolean }])[] = []; + allEntries(): ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean }])[] { + let res: ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean }])[] = []; for (let edit of this._edits) { if (edit._type === 1) { res.push([edit.from, edit.to, edit.options]); diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index 3b661348e19a8..bcd3c06748dcc 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -5,6 +5,7 @@ 'use strict'; +import { mergeSort } from 'vs/base/common/arrays'; import { getPathLabel } from 'vs/base/common/labels'; import { dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; @@ -27,7 +28,6 @@ import { emptyProgressRunner, IProgress, IProgressRunner } from 'vs/platform/pro import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { mergeSort } from 'vs/base/common/arrays'; abstract class Recording { @@ -335,12 +335,13 @@ export class BulkEdit { for (const edit of edits) { progress.report(undefined); + let overwrite = edit.options && edit.options.overwrite; if (edit.newUri && edit.oldUri) { - await this._textFileService.move(edit.oldUri, edit.newUri, edit.options && edit.options.override); + await this._textFileService.move(edit.oldUri, edit.newUri, overwrite); } else if (!edit.newUri && edit.oldUri) { await this._textFileService.delete(edit.oldUri, true); } else if (edit.newUri && !edit.oldUri) { - await this._fileService.createFile(edit.newUri, undefined, { overwrite: edit.options && edit.options.override }); + await this._fileService.createFile(edit.newUri, undefined, { overwrite }); } } } diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index add4566a3f62e..0d3bbc7d59eef 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -386,12 +386,12 @@ suite('ExtHostTypes', function () { const all = edit.allEntries(); assert.equal(all.length, 4); - function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI, { override?: boolean }]): thing is [URI, URI, { override?: boolean }] { + function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI, { overwrite?: boolean }]): thing is [URI, URI, { overwrite?: boolean }] { const [f, s] = thing; return URI.isUri(f) && URI.isUri(s); } - function isTextChange(thing: [URI, types.TextEdit[]] | [URI, URI, { override?: boolean }]): thing is [URI, types.TextEdit[]] { + function isTextChange(thing: [URI, types.TextEdit[]] | [URI, URI, { overwrite?: boolean }]): thing is [URI, types.TextEdit[]] { const [f, s] = thing; return URI.isUri(f) && Array.isArray(s); } From 519b82b5054be1d946953a5ac0d762c9c9596635 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Jun 2018 21:42:24 +1000 Subject: [PATCH 112/149] vscode-xterm@3.5.0-beta14 --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f2e1c40da1fad..f0c79849ac971 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "vscode-nsfw": "1.0.17", "vscode-ripgrep": "^1.0.1", "vscode-textmate": "^4.0.0-next.2", - "vscode-xterm": "3.5.0-beta13", + "vscode-xterm": "3.5.0-beta14", "yauzl": "^2.9.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 4f6c93b362736..4c087ecccf42a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6253,9 +6253,9 @@ vscode-textmate@^4.0.0-next.2: fast-plist "^0.1.2" oniguruma "^7.0.0" -vscode-xterm@3.5.0-beta13: - version "3.5.0-beta13" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.5.0-beta13.tgz#8fc24f6d7509e6119d8ec0deb07070e1ed86ddbc" +vscode-xterm@3.5.0-beta14: + version "3.5.0-beta14" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.5.0-beta14.tgz#3d55256e68b822cb528e64c281a786d2ac9db369" vso-node-api@^6.1.2-preview: version "6.1.2-preview" From 52e29e90ee8341e7188570cb4b28de6c76018bc2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Jun 2018 21:42:46 +1000 Subject: [PATCH 113/149] Update reaching into xterm.js internals to work after layering merge --- src/typings/vscode-xterm.d.ts | 36 +++++++++++-------- .../electron-browser/terminalConfigHelper.ts | 6 ++-- .../electron-browser/terminalInstance.ts | 12 +++---- .../terminal/node/terminalCommandTracker.ts | 8 ++--- .../test/node/terminalCommandTracker.test.ts | 20 +++++------ 5 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index 356aee471e3a7..8c3b106917b69 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -671,17 +671,29 @@ declare module 'vscode-xterm' { // Modifications to official .d.ts below declare module 'vscode-xterm' { interface Terminal { - buffer: { - y: number; - ybase: number; - ydisp: number; - x: number; - }; + _core: { + buffer: { + y: number; + ybase: number; + ydisp: number; + x: number; + }; + + /** + * Emit an event on the terminal. + */ + emit(type: string, data: any): void; + + charMeasure?: { height: number, width: number }; + + renderer: { + _renderLayers: any[]; + onIntersectionChange: any; + } + } - /** - * Emit an event on the terminal. - */ - emit(type: string, data: any): void; + webLinksInit(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void; + winptyCompatInit(): void; /** * Find the next instance of the term, then scroll to and select it. If it @@ -698,9 +710,5 @@ declare module 'vscode-xterm' { * @return Whether a result was found. */ findPrevious(term: string): boolean; - - webLinksInit(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void; - winptyCompatInit(): void; - charMeasure?: { height: number, width: number }; } } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index 2d887bcd678ee..4d4f6645ad04d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -137,14 +137,14 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { // Get the character dimensions from xterm if it's available if (xterm) { - if (xterm.charMeasure && xterm.charMeasure.width && xterm.charMeasure.height) { + if (xterm._core.charMeasure && xterm._core.charMeasure.width && xterm._core.charMeasure.height) { return { fontFamily, fontSize, letterSpacing, lineHeight, - charHeight: xterm.charMeasure.height, - charWidth: xterm.charMeasure.width + charHeight: xterm._core.charMeasure.height, + charWidth: xterm._core.charMeasure.width }; } } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 7f7ebf0366786..6203b5c5c9771 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -232,7 +232,7 @@ export class TerminalInstance implements ITerminalInstance { // it gets removed and then added back to the DOM (resetting scrollTop to 0). // Upstream issue: https://github.com/sourcelair/xterm.js/issues/291 if (this._xterm) { - this._xterm.emit('scroll', this._xterm.buffer.ydisp); + this._xterm.emit('scroll', this._xterm._core.buffer.ydisp); } } @@ -460,7 +460,7 @@ export class TerminalInstance implements ITerminalInstance { private _measureRenderTime(): void { const frameTimes: number[] = []; - const textRenderLayer = (this._xterm).renderer._renderLayers[0]; + const textRenderLayer = this._xterm._core.renderer._renderLayers[0]; const originalOnGridChanged = textRenderLayer.onGridChanged; const evaluateCanvasRenderer = () => { @@ -575,7 +575,7 @@ export class TerminalInstance implements ITerminalInstance { this._xtermElement = null; } if (this._xterm) { - const buffer = (this._xterm.buffer); + const buffer = (this._xterm._core.buffer); this._sendLineData(buffer, buffer.ybase + buffer.y); this._xterm.dispose(); this._xterm = null; @@ -648,7 +648,7 @@ export class TerminalInstance implements ITerminalInstance { // necessary if the number of rows in the terminal has decreased while it was in the // background since scrollTop changes take no effect but the terminal's position does // change since the number of visible rows decreases. - this._xterm.emit('scroll', this._xterm.buffer.ydisp); + this._xterm.emit('scroll', this._xterm._core.buffer.ydisp); if (this._container && this._container.parentElement) { // Force a layout when the instance becomes invisible. This is particularly important // for ensuring that terminals that are created in the background by an extension will @@ -848,7 +848,7 @@ export class TerminalInstance implements ITerminalInstance { } private _onLineFeed(): void { - const buffer = (this._xterm.buffer); + const buffer = (this._xterm._core.buffer); const newLine = buffer.lines.get(buffer.ybase + buffer.y); if (!newLine.isWrapped) { this._sendLineData(buffer, buffer.ybase + buffer.y - 1); @@ -974,7 +974,7 @@ export class TerminalInstance implements ITerminalInstance { // on Winodws/Linux would fire an event saying that the terminal was not visible. // This should only force a refresh if one is needed. if (this._xterm.getOption('rendererType') === 'canvas') { - (this._xterm).renderer.onIntersectionChange({ intersectionRatio: 1 }); + this._xterm._core.renderer.onIntersectionChange({ intersectionRatio: 1 }); } } } diff --git a/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts b/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts index bdde9f6e0b9cb..6e8f0335b8b36 100644 --- a/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts +++ b/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts @@ -49,7 +49,7 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa } private _onEnter(): void { - if (this._xterm.buffer.x >= MINIMUM_PROMPT_LENGTH) { + if (this._xterm._core.buffer.x >= MINIMUM_PROMPT_LENGTH) { this._xterm.addMarker(0); } } @@ -176,7 +176,7 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa private _getLine(marker: IMarker | Boundary): number { // Use the _second last_ row as the last row is likely the prompt if (marker === Boundary.Bottom) { - return this._xterm.buffer.ybase + this._xterm.rows - 1; + return this._xterm._core.buffer.ybase + this._xterm.rows - 1; } if (marker === Boundary.Top) { @@ -236,10 +236,10 @@ export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposa if (this._currentMarker === Boundary.Bottom) { return 0; } else if (this._currentMarker === Boundary.Top) { - return 0 - (this._xterm.buffer.ybase + this._xterm.buffer.y); + return 0 - (this._xterm._core.buffer.ybase + this._xterm._core.buffer.y); } else { let offset = this._getLine(this._currentMarker); - offset -= this._xterm.buffer.ybase + this._xterm.buffer.y; + offset -= this._xterm._core.buffer.ybase + this._xterm._core.buffer.y; return offset; } } diff --git a/src/vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts b/src/vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts index 003f76b383bf5..abc4abc1d10db 100644 --- a/src/vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts +++ b/src/vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts @@ -62,24 +62,24 @@ suite('Workbench - TerminalCommandTracker', () => { for (let i = 0; i < 20; i++) { syncWrite(xterm, `\r\n`); } - assert.equal(xterm.buffer.ybase, 20); - assert.equal(xterm.buffer.ydisp, 20); + assert.equal(xterm._core.buffer.ybase, 20); + assert.equal(xterm._core.buffer.ydisp, 20); // Scroll to marker commandTracker.scrollToPreviousCommand(); - assert.equal(xterm.buffer.ydisp, 9); + assert.equal(xterm._core.buffer.ydisp, 9); // Scroll to top boundary commandTracker.scrollToPreviousCommand(); - assert.equal(xterm.buffer.ydisp, 0); + assert.equal(xterm._core.buffer.ydisp, 0); // Scroll to marker commandTracker.scrollToNextCommand(); - assert.equal(xterm.buffer.ydisp, 9); + assert.equal(xterm._core.buffer.ydisp, 9); // Scroll to bottom boundary commandTracker.scrollToNextCommand(); - assert.equal(xterm.buffer.ydisp, 20); + assert.equal(xterm._core.buffer.ydisp, 20); }); test('should select to the next and previous commands', () => { (window).matchMedia = () => { @@ -98,8 +98,8 @@ suite('Workbench - TerminalCommandTracker', () => { assert.equal(xterm.markers[1].line, 11); syncWrite(xterm, '\n\r3'); - assert.equal(xterm.buffer.ybase, 3); - assert.equal(xterm.buffer.ydisp, 3); + assert.equal(xterm._core.buffer.ybase, 3); + assert.equal(xterm._core.buffer.ydisp, 3); assert.equal(xterm.getSelection(), ''); commandTracker.selectToPreviousCommand(); @@ -128,8 +128,8 @@ suite('Workbench - TerminalCommandTracker', () => { assert.equal(xterm.markers[1].line, 11); syncWrite(xterm, '\n\r3'); - assert.equal(xterm.buffer.ybase, 3); - assert.equal(xterm.buffer.ydisp, 3); + assert.equal(xterm._core.buffer.ybase, 3); + assert.equal(xterm._core.buffer.ydisp, 3); assert.equal(xterm.getSelection(), ''); commandTracker.selectToPreviousLine(); From 94fec827fee54d5e6637947372244dba89e769fb Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Jun 2018 21:55:00 +1000 Subject: [PATCH 114/149] Fix tests --- src/typings/vscode-xterm.d.ts | 42 ++++++++++--------- .../test/node/terminalCommandTracker.test.ts | 12 ++++-- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index 8c3b106917b69..de61f79c19569 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -670,27 +670,29 @@ declare module 'vscode-xterm' { // Modifications to official .d.ts below declare module 'vscode-xterm' { + interface TerminalCore { + buffer: { + y: number; + ybase: number; + ydisp: number; + x: number; + }; + + /** + * Emit an event on the terminal. + */ + emit(type: string, data: any): void; + + charMeasure?: { height: number, width: number }; + + renderer: { + _renderLayers: any[]; + onIntersectionChange: any; + }; + } + interface Terminal { - _core: { - buffer: { - y: number; - ybase: number; - ydisp: number; - x: number; - }; - - /** - * Emit an event on the terminal. - */ - emit(type: string, data: any): void; - - charMeasure?: { height: number, width: number }; - - renderer: { - _renderLayers: any[]; - onIntersectionChange: any; - } - } + _core: TerminalCore; webLinksInit(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void; winptyCompatInit(): void; diff --git a/src/vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts b/src/vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts index abc4abc1d10db..495ffc45da234 100644 --- a/src/vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts +++ b/src/vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts @@ -4,19 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Terminal } from 'vscode-xterm'; +import { Terminal, TerminalCore } from 'vscode-xterm'; import { TerminalCommandTracker } from 'vs/workbench/parts/terminal/node/terminalCommandTracker'; import { isWindows } from 'vs/base/common/platform'; -interface TestTerminal extends Terminal { +interface TestTerminalCore extends TerminalCore { writeBuffer: string[]; _innerWrite(): void; } +interface TestTerminal extends Terminal { + _core: TestTerminalCore; +} + function syncWrite(term: TestTerminal, data: string): void { // Terminal.write is asynchronous - term.writeBuffer.push(data); - term._innerWrite(); + term._core.writeBuffer.push(data); + term._core._innerWrite(); } const ROWS = 10; From 967451ed9a96703e5bcac7a69b2f4d27fa2f5889 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Jun 2018 12:45:19 +0200 Subject: [PATCH 115/149] propagate isReadonly properly --- src/vs/platform/files/common/files.ts | 10 +++++----- .../services/files/electron-browser/fileService.ts | 2 ++ .../files/electron-browser/remoteFileService.ts | 4 +++- .../services/textfile/common/textFileEditorModel.ts | 6 ++++-- .../textfile/electron-browser/textFileService.ts | 1 + 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 9ab1c0be2955b..39ed62b06f6d8 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -399,6 +399,11 @@ export interface IBaseStat { * current state of the file or directory. */ etag: string; + + /** + * The resource is readonly. + */ + isReadonly?: boolean; } /** @@ -412,11 +417,6 @@ export interface IFileStat extends IBaseStat { */ isDirectory: boolean; - /** - * The resource is readonly. - */ - isReadonly?: boolean; - /** * The resource is a symbolic link. */ diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index b79030bd583d3..75a2c671e5249 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -273,6 +273,7 @@ export class FileService implements IFileService { mtime: streamContent.mtime, etag: streamContent.etag, encoding: streamContent.encoding, + isReadonly: streamContent.isReadonly, value: '' }; @@ -302,6 +303,7 @@ export class FileService implements IFileService { mtime: void 0, etag: void 0, encoding: void 0, + isReadonly: false, value: void 0 }; diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index d03b24db8bd82..610f6c09692e3 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -412,6 +412,7 @@ export class RemoteFileService extends FileService { name: fileStat.name, etag: fileStat.etag, mtime: fileStat.mtime, + isReadonly: fileStat.isReadonly }; }); }); @@ -499,7 +500,8 @@ export class RemoteFileService extends FileService { etag: content.etag, mtime: content.mtime, name: content.name, - resource: content.resource + resource: content.resource, + isReadonly: content.isReadonly }; content.value.on('data', chunk => result.value += chunk); content.value.on('error', reject); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index b6dc162085208..5d4c949604676 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -310,7 +310,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil mtime: Date.now(), etag: void 0, value: createTextBufferFactory(''), /* will be filled later from backup */ - encoding: this.fileService.encoding.getWriteEncoding(this.resource, this.preferredEncoding) + encoding: this.fileService.encoding.getWriteEncoding(this.resource, this.preferredEncoding), + isReadonly: false }; return this.loadWithContent(content, backup); @@ -406,7 +407,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil etag: content.etag, isDirectory: false, isSymbolicLink: false, - children: void 0 + children: void 0, + isReadonly: content.isReadonly }; this.updateLastResolvedDiskStat(resolvedStat); diff --git a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts index fcb157c88074e..830c9d7404223 100644 --- a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts @@ -63,6 +63,7 @@ export class TextFileService extends AbstractTextFileService { mtime: streamContent.mtime, etag: streamContent.etag, encoding: streamContent.encoding, + isReadonly: streamContent.isReadonly, value: res }; return r; From b7e2e55f6df49bdee4805787aa55d6b1fde08466 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Jun 2018 14:00:25 +0200 Subject: [PATCH 116/149] Fix #47532 --- .../extensionManagement/node/extensionManagementService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index c73240422680f..611209b69266a 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -446,7 +446,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private extractAndRename(id: string, zipPath: string, extractPath: string, renamePath: string): TPromise { return this.extract(id, zipPath, extractPath) - .then(() => this.rename(id, extractPath, renamePath, Date.now() + (30 * 1000) /* Retry for 30 seconds */) + .then(() => this.rename(id, extractPath, renamePath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */) .then( () => this.logService.info('Renamed to', renamePath), e => { From f607ee44811ddb1a871919d58e4e6c5f15303600 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Jun 2018 22:13:26 +1000 Subject: [PATCH 117/149] Type terminal smoke tests --- src/typings/vscode-xterm.d.ts | 5 +++++ src/vs/platform/driver/electron-browser/driver.ts | 11 ++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index de61f79c19569..8661eec2dd73d 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -676,8 +676,13 @@ declare module 'vscode-xterm' { ybase: number; ydisp: number; x: number; + lines: any[]; + + translateBufferLineToString(lineIndex: number, trimRight: boolean): string; }; + send(text: string): void; + /** * Emit an event on the terminal. */ diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index 5ba3a7b944490..6c723a9918187 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -13,6 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { getTopLeftOffset, getClientArea } from 'vs/base/browser/dom'; import * as electron from 'electron'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { Terminal } from 'vscode-xterm'; function serializeElement(element: Element, recursive: boolean): IElement { const attributes = Object.create(null); @@ -172,7 +173,7 @@ class WindowDriver implements IWindowDriver { throw new Error('Terminal not found: ' + selector); } - const xterm = (element as any).xterm; + const xterm: Terminal = (element as any).xterm; if (!xterm) { throw new Error('Xterm not found: ' + selector); @@ -180,8 +181,8 @@ class WindowDriver implements IWindowDriver { const lines: string[] = []; - for (let i = 0; i < xterm.buffer.lines.length; i++) { - lines.push(xterm.buffer.translateBufferLineToString(i, true)); + for (let i = 0; i < xterm._core.buffer.lines.length; i++) { + lines.push(xterm._core.buffer.translateBufferLineToString(i, true)); } return lines; @@ -194,13 +195,13 @@ class WindowDriver implements IWindowDriver { throw new Error('Element not found'); } - const xterm = (element as any).xterm; + const xterm: Terminal = (element as any).xterm; if (!xterm) { throw new Error('Xterm not found'); } - xterm.send(text); + xterm._core.send(text); } async openDevTools(): TPromise { From 341fc24bbc41834b7501f276d9f6775c9cdac569 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 20 Jun 2018 23:08:24 +1000 Subject: [PATCH 118/149] Don't let DOM renderer render outside cols Fixes #51479 --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f0c79849ac971..7e92f6392d7ed 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "vscode-nsfw": "1.0.17", "vscode-ripgrep": "^1.0.1", "vscode-textmate": "^4.0.0-next.2", - "vscode-xterm": "3.5.0-beta14", + "vscode-xterm": "3.5.0-beta15", "yauzl": "^2.9.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 4c087ecccf42a..1a8735b5551fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6253,9 +6253,9 @@ vscode-textmate@^4.0.0-next.2: fast-plist "^0.1.2" oniguruma "^7.0.0" -vscode-xterm@3.5.0-beta14: - version "3.5.0-beta14" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.5.0-beta14.tgz#3d55256e68b822cb528e64c281a786d2ac9db369" +vscode-xterm@3.5.0-beta15: + version "3.5.0-beta15" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.5.0-beta15.tgz#3098121b7f46048254e9e20905fea6cf792f6bd1" vso-node-api@^6.1.2-preview: version "6.1.2-preview" From 76cdeb794892e1fa7a63a1f069922c4e7cc9c54b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Jun 2018 15:50:10 +0200 Subject: [PATCH 119/149] preferences editor needs a min height --- src/vs/workbench/parts/preferences/browser/preferencesEditor.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 0eb68416f9403..61669bfa5e3b0 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -82,6 +82,8 @@ export class PreferencesEditor extends BaseEditor { set minimumWidth(value: number) { /*noop*/ } set maximumWidth(value: number) { /*noop*/ } + readonly minimumHeight = 260; + private _onDidCreateWidget = new Emitter<{ width: number; height: number; }>(); readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = this._onDidCreateWidget.event; From 4dbe064b416f2ebd48883ddf3685ad8324a10f65 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Jun 2018 15:55:05 +0200 Subject: [PATCH 120/149] better grid 2x2 fixes #52333 --- src/vs/base/browser/ui/grid/grid.ts | 3 +- src/vs/base/browser/ui/grid/gridview.ts | 98 +++++++++++++++++++++---- 2 files changed, 85 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index e9b941a942657..15e572c303001 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -467,7 +467,6 @@ export class SerializableGrid extends Grid { result.orientation = orientation; result.restoreViews(firstLeaf.view, orientation, root); result.initialLayoutContext = { width, height, root }; - result.gridview.trySet2x2(); return result; } @@ -496,6 +495,8 @@ export class SerializableGrid extends Grid { this.restoreViewsSize([], this.initialLayoutContext.root, this.orientation, widthScale, heightScale); this.initialLayoutContext = undefined; + + this.gridview.trySet2x2(); } } diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 9c04e1a08085a..6f40d22b0a0d9 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -75,7 +75,7 @@ export interface IGridViewOptions { class BranchNode implements ISplitView, IDisposable { readonly element: HTMLElement; - readonly children: Node[]; + readonly children: Node[] = []; private splitview: SplitView; private _size: number; @@ -127,8 +127,9 @@ class BranchNode implements ISplitView, IDisposable { return this.orientation === Orientation.HORIZONTAL ? this.maximumSize : this.maximumOrthogonalSize; } - private _onDidChange: Emitter; - get onDidChange(): Event { return this._onDidChange.event; } + private _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; + private childrenChangeDisposable: IDisposable = EmptyDisposable; private _onDidSashReset = new Emitter(); @@ -151,9 +152,6 @@ class BranchNode implements ISplitView, IDisposable { this._size = size; this._orthogonalSize = orthogonalSize; - this._onDidChange = new Emitter(); - this.children = []; - this.element = $('.monaco-grid-branch-node'); this.splitview = new SplitView(this.element, { orientation, styles }); this.splitview.layout(size); @@ -297,11 +295,44 @@ class BranchNode implements ISplitView, IDisposable { return EmptyDisposable; } + const [firstChild, secondChild] = this.children; + const [otherFirstChild, otherSecondChild] = other.children; + + if (!(firstChild instanceof LeafNode) || !(secondChild instanceof LeafNode)) { + return EmptyDisposable; + } + + if (!(otherFirstChild instanceof LeafNode) || !(otherSecondChild instanceof LeafNode)) { + return EmptyDisposable; + } + + if (this.orientation === Orientation.VERTICAL) { + secondChild.linkedWidthNode = otherFirstChild.linkedHeightNode = firstChild; + firstChild.linkedWidthNode = otherSecondChild.linkedHeightNode = secondChild; + otherSecondChild.linkedWidthNode = firstChild.linkedHeightNode = otherFirstChild; + otherFirstChild.linkedWidthNode = secondChild.linkedHeightNode = otherSecondChild; + } else { + otherFirstChild.linkedWidthNode = secondChild.linkedHeightNode = firstChild; + otherSecondChild.linkedWidthNode = firstChild.linkedHeightNode = secondChild; + firstChild.linkedWidthNode = otherSecondChild.linkedHeightNode = otherFirstChild; + secondChild.linkedWidthNode = otherFirstChild.linkedHeightNode = otherSecondChild; + } + const mySash = this.splitview.sashes[0]; const otherSash = other.splitview.sashes[0]; mySash.linkedSash = otherSash; otherSash.linkedSash = mySash; - return toDisposable(() => mySash.linkedSash = otherSash.linkedSash = undefined); + + this._onDidChange.fire(); + other._onDidChange.fire(); + + return toDisposable(() => { + mySash.linkedSash = otherSash.linkedSash = undefined; + firstChild.linkedHeightNode = firstChild.linkedWidthNode = undefined; + secondChild.linkedHeightNode = secondChild.linkedWidthNode = undefined; + otherFirstChild.linkedHeightNode = otherFirstChild.linkedWidthNode = undefined; + otherSecondChild.linkedHeightNode = otherSecondChild.linkedWidthNode = undefined; + }); } dispose(): void { @@ -326,12 +357,37 @@ class LeafNode implements ISplitView, IDisposable { readonly onDidSashReset: Event = Event.None; + private _onDidLinkedWidthNodeChange = new Relay(); + private _linkedWidthNode: LeafNode | undefined = undefined; + get linkedWidthNode(): LeafNode | undefined { return this._linkedWidthNode; } + set linkedWidthNode(node: LeafNode | undefined) { + this._onDidLinkedWidthNodeChange.input = node ? node._onDidViewChange : Event.None; + this._linkedWidthNode = node; + this._onDidSetLinkedNode.fire(); + } + + private _onDidLinkedHeightNodeChange = new Relay(); + private _linkedHeightNode: LeafNode | undefined = undefined; + get linkedHeightNode(): LeafNode | undefined { return this._linkedHeightNode; } + set linkedHeightNode(node: LeafNode | undefined) { + this._onDidLinkedHeightNodeChange.input = node ? node._onDidViewChange : Event.None; + this._linkedHeightNode = node; + this._onDidSetLinkedNode.fire(); + } + + private _onDidSetLinkedNode = new Emitter(); + private _onDidViewChange: Event; + readonly onDidChange: Event; + constructor( readonly view: IView, readonly orientation: Orientation, orthogonalSize: number = 0 ) { this._orthogonalSize = orthogonalSize; + + this._onDidViewChange = mapEvent(this.view.onDidChange, this.orientation === Orientation.HORIZONTAL ? e => e && e.width : e => e && e.height); + this.onDidChange = anyEvent(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event); } get width(): number { @@ -346,24 +402,36 @@ class LeafNode implements ISplitView, IDisposable { return this.view.element; } + private get minimumWidth(): number { + return this.linkedWidthNode ? Math.max(this.linkedWidthNode.view.minimumWidth, this.view.minimumWidth) : this.view.minimumWidth; + } + + private get maximumWidth(): number { + return this.linkedWidthNode ? Math.min(this.linkedWidthNode.view.maximumWidth, this.view.maximumWidth) : this.view.maximumWidth; + } + + private get minimumHeight(): number { + return this.linkedHeightNode ? Math.max(this.linkedHeightNode.view.minimumHeight, this.view.minimumHeight) : this.view.minimumHeight; + } + + private get maximumHeight(): number { + return this.linkedHeightNode ? Math.min(this.linkedHeightNode.view.maximumHeight, this.view.maximumHeight) : this.view.maximumHeight; + } + get minimumSize(): number { - return this.orientation === Orientation.HORIZONTAL ? this.view.minimumHeight : this.view.minimumWidth; + return this.orientation === Orientation.HORIZONTAL ? this.minimumHeight : this.minimumWidth; } get maximumSize(): number { - return this.orientation === Orientation.HORIZONTAL ? this.view.maximumHeight : this.view.maximumWidth; + return this.orientation === Orientation.HORIZONTAL ? this.maximumHeight : this.maximumWidth; } get minimumOrthogonalSize(): number { - return this.orientation === Orientation.HORIZONTAL ? this.view.minimumWidth : this.view.minimumHeight; + return this.orientation === Orientation.HORIZONTAL ? this.minimumWidth : this.minimumHeight; } get maximumOrthogonalSize(): number { - return this.orientation === Orientation.HORIZONTAL ? this.view.maximumWidth : this.view.maximumHeight; - } - - get onDidChange(): Event { - return mapEvent(this.view.onDidChange, this.orientation === Orientation.HORIZONTAL ? e => e && e.width : e => e && e.height); + return this.orientation === Orientation.HORIZONTAL ? this.maximumWidth : this.maximumHeight; } set orthogonalStartSash(sash: Sash) { From 991b74bceda7eb5f367ab31c864b8905e2da808a Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Jun 2018 15:56:34 +0200 Subject: [PATCH 121/149] debug: stop support for debug.hideActionBar fixes #52438 --- src/vs/workbench/parts/debug/browser/debugActionsWidget.ts | 3 +-- src/vs/workbench/parts/debug/common/debug.ts | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts index 15a5f4e83a99c..4a7902f94ecdc 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts @@ -93,8 +93,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi this.updateScheduler = new RunOnceScheduler(() => { const state = this.debugService.state; const toolBarLocation = this.configurationService.getValue('debug').toolBarLocation; - if (state === State.Inactive || this.configurationService.getValue('debug').hideActionBar - || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { + if (state === State.Inactive || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { return this.hide(); } diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 11a7f7d80449f..16fb543aa5ff1 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -355,7 +355,6 @@ export interface IDebugConfiguration { openDebug: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart' | 'openOnDebugBreak'; openExplorerOnEnd: boolean; inlineValues: boolean; - hideActionBar: boolean; toolBarLocation: 'floating' | 'docked' | 'hidden'; showInStatusBar: 'never' | 'always' | 'onFirstSessionStart'; internalConsoleOptions: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart'; From 257ae9f76c28520cc38766b2093c42adf53974b3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 15:57:38 +0200 Subject: [PATCH 122/149] add ignoreIfExists-option #10659 --- .../src/singlefolder-tests/workspace.test.ts | 8 ++++++++ src/vs/editor/common/modes.ts | 2 +- src/vs/monaco.d.ts | 3 ++- src/vs/vscode.proposed.d.ts | 2 +- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- src/vs/workbench/api/node/extHostTypes.ts | 12 ++++++------ .../bulkEdit/electron-browser/bulkEditService.ts | 5 ++++- 7 files changed, 23 insertions(+), 11 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 03b5f98cad5e8..d646e3579423b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -682,4 +682,12 @@ suite('workspace-namespace', () => { // todo@ben // assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), ''); }); + + test('WorkspaceEdit: create & ignoreIfExists', async function () { + let docUri = await createRandomFile('before'); + let we = new vscode.WorkspaceEdit(); + we.createFile(docUri, { ignoreIfExists: true }); + assert.ok(await vscode.workspace.applyEdit(we)); + assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), 'before'); + }); }); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 6c79dab73e530..ac7a7e3e5796f 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -899,7 +899,7 @@ export function isResourceTextEdit(thing: any): thing is ResourceTextEdit { export interface ResourceFileEdit { oldUri: URI; newUri: URI; - options: { overwrite: boolean }; + options: { overwrite?: boolean, ignoreIfExists?: boolean }; } export interface ResourceTextEdit { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 37a856ec4f338..fbd261ef7ef6b 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5101,7 +5101,8 @@ declare namespace monaco.languages { oldUri: Uri; newUri: Uri; options: { - overwrite: boolean; + overwrite?: boolean; + ignoreIfExists?: boolean; }; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 840fd9027cb7b..3b46a0f02efd4 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -621,7 +621,7 @@ declare module 'vscode' { //#region joh: https://github.com/Microsoft/vscode/issues/10659 export interface WorkspaceEdit { - createFile(uri: Uri, options?: { overwrite?: boolean }): void; + createFile(uri: Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void; deleteFile(uri: Uri): void; renameFile(oldUri: Uri, newUri: Uri, options?: { overwrite?: boolean }): void; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 7407b8068acd2..b5a5b3d993314 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -766,7 +766,7 @@ export interface WorkspaceSymbolsDto extends IdObject { export interface ResourceFileEditDto { oldUri: UriComponents; newUri: UriComponents; - options: { overwrite?: boolean }; + options: { overwrite?: boolean, ignoreIfExists?: boolean }; } export interface ResourceTextEditDto { diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 2a629d5591871..ecbb8feec70a4 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -499,7 +499,7 @@ export interface IFileOperation { _type: 1; from: URI; to: URI; - options?: { overwrite?: boolean }; + options?: { overwrite?: boolean, ignoreIfExists?: boolean; }; } export interface IFileTextEdit { @@ -516,12 +516,12 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { this._edits.push({ _type: 1, from, to, options }); } - createFile(uri: vscode.Uri, options?: { overwrite?: boolean }): void { - this.renameFile(undefined, uri, options); + createFile(uri: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void { + this._edits.push({ _type: 1, from: undefined, to: uri, options }); } deleteFile(uri: vscode.Uri): void { - this.renameFile(uri, undefined); + this._edits.push({ _type: 1, from: uri, to: undefined }); } replace(uri: URI, range: Range, newText: string): void { @@ -593,8 +593,8 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { return values(textEdits); } - allEntries(): ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean }])[] { - let res: ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean }])[] = []; + allEntries(): ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean, ignoreIfExists?: boolean }])[] { + let res: ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean, ignoreIfExists?: boolean }])[] = []; for (let edit of this._edits) { if (edit._type === 1) { res.push([edit.from, edit.to, edit.options]); diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index bcd3c06748dcc..f3f62fdbf024b 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -341,7 +341,10 @@ export class BulkEdit { } else if (!edit.newUri && edit.oldUri) { await this._textFileService.delete(edit.oldUri, true); } else if (edit.newUri && !edit.oldUri) { - await this._fileService.createFile(edit.newUri, undefined, { overwrite }); + let ignoreIfExists = edit.options && edit.options.ignoreIfExists; + if (!ignoreIfExists || !await this._fileService.existsFile(edit.newUri)) { + await this._fileService.createFile(edit.newUri, undefined, { overwrite }); + } } } } From 48d37fc9762dbf2d2a7755966ebd9261ad28b286 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 16:07:19 +0200 Subject: [PATCH 123/149] some jsdoc #10659 --- src/vs/vscode.proposed.d.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 3b46a0f02efd4..63abc9c53fdd3 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -621,8 +621,29 @@ declare module 'vscode' { //#region joh: https://github.com/Microsoft/vscode/issues/10659 export interface WorkspaceEdit { + + /** + * Create a regular file. + * + * @param uri Uri of the new file.. + * @param options Defines if an existing file should be overwritten or be ignored. + */ createFile(uri: Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void; + + /** + * Delete a file or folder. + * + * @param uri The uri of the file that is to be deleted. + */ deleteFile(uri: Uri): void; + + /** + * Rename a file or folder. + * + * @param oldUri The existing file. + * @param newUri The new location. + * @param options Defines if existing files should be overwritten. + */ renameFile(oldUri: Uri, newUri: Uri, options?: { overwrite?: boolean }): void; // replaceText(uri: Uri, range: Range, newText: string): void; From b74931813c63053544689d1b586d737f847287f6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 16:07:59 +0200 Subject: [PATCH 124/149] _allEntries isn't API, #10659 --- src/vs/workbench/api/node/extHostTypeConverters.ts | 2 +- src/vs/workbench/api/node/extHostTypes.ts | 2 +- .../test/electron-browser/api/extHostTypes.test.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 2c5a3cc0babbe..96b275af75f39 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -274,7 +274,7 @@ export namespace WorkspaceEdit { const result: WorkspaceEditDto = { edits: [] }; - for (const entry of (value as types.WorkspaceEdit).allEntries()) { + for (const entry of (value as types.WorkspaceEdit)._allEntries()) { const [uri, uriOrEdits] = entry; if (Array.isArray(uriOrEdits)) { // text edits diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index ecbb8feec70a4..677f2a9ccf497 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -593,7 +593,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { return values(textEdits); } - allEntries(): ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean, ignoreIfExists?: boolean }])[] { + _allEntries(): ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean, ignoreIfExists?: boolean }])[] { let res: ([URI, TextEdit[]] | [URI, URI, { overwrite?: boolean, ignoreIfExists?: boolean }])[] = []; for (let edit of this._edits) { if (edit._type === 1) { diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index 0d3bbc7d59eef..d14a9a0469ec2 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -383,7 +383,7 @@ suite('ExtHostTypes', function () { edit.replace(URI.parse('foo:a'), new types.Range(2, 1, 2, 1), 'bar'); edit.replace(URI.parse('foo:b'), new types.Range(3, 1, 3, 1), 'bazz'); - const all = edit.allEntries(); + const all = edit._allEntries(); assert.equal(all.length, 4); function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI, { overwrite?: boolean }]): thing is [URI, URI, { overwrite?: boolean }] { @@ -418,8 +418,8 @@ suite('ExtHostTypes', function () { edit.insert(uri, new types.Position(0, 0), 'Hello'); edit.insert(uri, new types.Position(0, 0), 'Foo'); - assert.equal(edit.allEntries().length, 2); - let [first, second] = edit.allEntries(); + assert.equal(edit._allEntries().length, 2); + let [first, second] = edit._allEntries(); assert.equal((first as [URI, types.TextEdit[]])[1][0].newText, 'Hello'); assert.equal((second as [URI, types.TextEdit[]])[1][0].newText, 'Foo'); }); From 0a35cbfcf422054090971f78cf3a357cb42c3bae Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Jun 2018 16:13:38 +0200 Subject: [PATCH 125/149] fixes #52428 --- .../parts/debug/browser/debugStatus.ts | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugStatus.ts b/src/vs/workbench/parts/debug/browser/debugStatus.ts index f72afca4b5d83..755b562c84ee1 100644 --- a/src/vs/workbench/parts/debug/browser/debugStatus.ts +++ b/src/vs/workbench/parts/debug/browser/debugStatus.ts @@ -45,15 +45,11 @@ export class DebugStatus extends Themable implements IStatusbarItem { this.toDispose.push(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('debug.showInStatusBar')) { this.showInStatusBar = configurationService.getValue('debug').showInStatusBar; - if (this.showInStatusBar === 'never' && this.statusBarItem) { - this.statusBarItem.hidden = true; - } else { - if (this.statusBarItem) { - this.statusBarItem.hidden = false; - } - if (this.showInStatusBar === 'always') { - this.doRender(); - } + if (this.showInStatusBar === 'always') { + this.doRender(); + } + if (this.statusBarItem) { + dom.toggleClass(this.statusBarItem, 'hidden', this.showInStatusBar === 'never'); } } })); @@ -99,11 +95,10 @@ export class DebugStatus extends Themable implements IStatusbarItem { if (this.label && this.statusBarItem) { const manager = this.debugService.getConfigurationManager(); const name = manager.selectedConfiguration.name; - if (name && manager.selectedConfiguration.launch) { - this.statusBarItem.style.display = 'block'; + const nameAndLaunchPresent = name && manager.selectedConfiguration.launch; + dom.toggleClass(this.statusBarItem, 'hidden', this.showInStatusBar === 'never' || !nameAndLaunchPresent); + if (nameAndLaunchPresent) { this.label.textContent = manager.getLaunches().length > 1 ? `${name} (${manager.selectedConfiguration.launch.name})` : name; - } else { - this.statusBarItem.style.display = 'none'; } } } From edb49f690017abfbae10370750f6494f5fee6732 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Jun 2018 15:38:42 +0200 Subject: [PATCH 126/149] Fix focus behavior (#49340) --- .../workbench/browser/parts/quickinput/quickInput.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index be1a419ed6045..13b3250cf6c0a 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -841,8 +841,12 @@ export class QuickInputService extends Component implements IQuickInputService { break; case KeyCode.Tab: if (!event.altKey && !event.ctrlKey && !event.metaKey) { - const inputs = [].slice.call(container.querySelectorAll('input')) - .filter(input => input.style.display !== 'none'); + const inputs = [].slice.call(container.querySelectorAll('.action-label.icon')); + if (container.classList.contains('show-checkboxes')) { + inputs.push(...[].slice.call(container.querySelectorAll('input'))); + } else { + inputs.push(...[].slice.call(container.querySelectorAll('input[type=text]'))); + } if (event.shiftKey && event.target === inputs[0]) { dom.EventHelper.stop(e, true); inputs[inputs.length - 1].focus(); @@ -1062,7 +1066,7 @@ export class QuickInputService extends Component implements IQuickInputService { (item as ActionItem).getAction().enabled = enabled; } this.ui.checkAll.disabled = !enabled; - this.ui.inputBox.enabled = enabled; + // this.ui.inputBox.enabled = enabled; Avoid loosing focus. this.ok.enabled = enabled; this.ui.list.enabled = enabled; } From b705856d82776a547e4605b287b33156a91d1896 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Jun 2018 16:23:01 +0200 Subject: [PATCH 127/149] More title polish (#49340) --- .../browser/parts/quickinput/quickInput.css | 15 ++++----------- .../browser/parts/quickinput/quickInput.ts | 5 +++++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.css b/src/vs/workbench/browser/parts/quickinput/quickInput.css index c7301f01e1cbb..fbcdcbe684c31 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.css +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.css @@ -18,7 +18,7 @@ .quick-input-left-action-bar { display: flex; - margin-left: 6px; + margin-left: 4px; flex: 1; } @@ -26,10 +26,6 @@ justify-content: flex-start; } -.quick-input-left-action-bar .action-label.icon { - margin: 0 5px 0 0; -} - .quick-input-title { padding: 3px 0px; text-align: center; @@ -37,16 +33,13 @@ .quick-input-right-action-bar { display: flex; - margin-right: 6px; + margin-right: 4px; flex: 1; } -.quick-input-right-action-bar .action-label.icon { - margin: 0 0 0 5px; -} - .quick-input-titlebar .monaco-action-bar .action-label.icon { - width: 16px; + margin: 0; + width: 19px; height: 100%; background-position: center; background-repeat: no-repeat; diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 13b3250cf6c0a..23e9a96fc834d 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -40,6 +40,7 @@ import { ActionBar, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; import URI from 'vs/base/common/uri'; import { IdGenerator } from 'vs/base/common/idGenerator'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; const $ = dom.$; @@ -710,6 +711,7 @@ export class QuickInputService extends Component implements IQuickInputService { @IPartService private partService: IPartService, @IQuickOpenService private quickOpenService: IQuickOpenService, @IEditorGroupsService private editorGroupService: IEditorGroupsService, + @IKeybindingService private keybindingService: IKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService ) { @@ -1037,6 +1039,9 @@ export class QuickInputService extends Component implements IQuickInputService { this.ui.list.matchOnDetail = false; this.ui.ignoreFocusOut = false; + const keybinding = this.keybindingService.lookupKeybinding(BackAction.ID); + backButton.tooltip = keybinding ? localize('quickInput.backWithKeybinding', "Back ({0})", keybinding.getLabel()) : localize('quickInput.back', "Back"); + this.inQuickOpen('quickInput', true); this.ui.container.style.display = ''; From bd2a8b19c12cccb23a2d717dabf3c14a8f41ec2f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Jun 2018 16:10:48 +0200 Subject: [PATCH 128/149] tree: expose shouldToggleExpansion --- src/vs/base/parts/tree/browser/treeDefaults.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/base/parts/tree/browser/treeDefaults.ts b/src/vs/base/parts/tree/browser/treeDefaults.ts index 7e9a7a682a43a..7540ef4659899 100644 --- a/src/vs/base/parts/tree/browser/treeDefaults.ts +++ b/src/vs/base/parts/tree/browser/treeDefaults.ts @@ -170,7 +170,6 @@ export class DefaultController implements _.IController { protected onLeftClick(tree: _.ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean { const payload = { origin: origin, originalEvent: eventish }; const event = eventish; - const isDoubleClick = (origin === 'mouse' && event.detail === 2); if (tree.getInput() === element) { tree.clearFocus(payload); @@ -186,7 +185,7 @@ export class DefaultController implements _.IController { tree.setSelection([element], payload); tree.setFocus(element, payload); - if (this.openOnSingleClick || isDoubleClick || this.isClickOnTwistie(event)) { + if (this.shouldToggleExpansion(element, event, origin)) { if (tree.isExpanded(element)) { tree.collapse(element).done(null, errors.onUnexpectedError); } else { @@ -198,6 +197,11 @@ export class DefaultController implements _.IController { return true; } + protected shouldToggleExpansion(element: any, event: mouse.IMouseEvent, origin: string): boolean { + const isDoubleClick = (origin === 'mouse' && event.detail === 2); + return this.openOnSingleClick || isDoubleClick || this.isClickOnTwistie(event); + } + protected setOpenMode(openMode: OpenMode) { this.options.openMode = openMode; } From 2c3247dfc8e08b2516bdb31e1d8e1a9c653109c2 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Jun 2018 16:34:32 +0200 Subject: [PATCH 129/149] fix isClickOnTwistie --- .../base/parts/tree/browser/treeDefaults.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/vs/base/parts/tree/browser/treeDefaults.ts b/src/vs/base/parts/tree/browser/treeDefaults.ts index 7540ef4659899..1858b0dcaa87b 100644 --- a/src/vs/base/parts/tree/browser/treeDefaults.ts +++ b/src/vs/base/parts/tree/browser/treeDefaults.ts @@ -211,13 +211,20 @@ export class DefaultController implements _.IController { } protected isClickOnTwistie(event: mouse.IMouseEvent): boolean { - const target = event.target as HTMLElement; + let element = event.target as HTMLElement; - // There is no way to find out if the ::before element is clicked where - // the twistie is drawn, but the
element in the - // tree item is the only thing we get back as target when the user clicks - // on the twistie. - return target && dom.hasClass(target, 'content') && dom.hasClass(target.parentElement, 'monaco-tree-row'); + if (!dom.hasClass(element, 'content')) { + return false; + } + + const twistieStyle = window.getComputedStyle(element, ':before'); + + if (twistieStyle.backgroundImage === 'none' || twistieStyle.display === 'none') { + return false; + } + + const twistieWidth = parseInt(twistieStyle.width) + parseInt(twistieStyle.paddingRight); + return event.browserEvent.offsetX <= twistieWidth; } public onContextMenu(tree: _.ITree, element: any, event: _.ContextMenuEvent): boolean { From b281f732a77f760d877dd94dda04cf22ebf56d67 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 20 Jun 2018 16:46:14 +0200 Subject: [PATCH 130/149] Remove disclaimer (#49340) --- src/vs/vscode.proposed.d.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 63abc9c53fdd3..f90fa8bf930ac 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -525,14 +525,8 @@ declare module 'vscode' { export const quickInputBackButton: QuickInputButton; - /** - * Implementation incomplete. See #49340. - */ export function createQuickPick(): QuickPick; - /** - * Implementation incomplete. See #49340. - */ export function createInputBox(): InputBox; } From 3beb4f33066878bf0f398299f86ca7ad09c6329b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 20 Jun 2018 16:57:24 +0200 Subject: [PATCH 131/149] Fix #34130 --- src/vs/base/parts/tree/browser/treeDefaults.ts | 2 +- src/vs/workbench/browser/parts/views/customView.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/base/parts/tree/browser/treeDefaults.ts b/src/vs/base/parts/tree/browser/treeDefaults.ts index 1858b0dcaa87b..b5c4ca60e62da 100644 --- a/src/vs/base/parts/tree/browser/treeDefaults.ts +++ b/src/vs/base/parts/tree/browser/treeDefaults.ts @@ -168,8 +168,8 @@ export class DefaultController implements _.IController { } protected onLeftClick(tree: _.ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean { - const payload = { origin: origin, originalEvent: eventish }; const event = eventish; + const payload = { origin: origin, originalEvent: eventish, didClickOnTwistie: this.isClickOnTwistie(event) }; if (tree.getInput() === element) { tree.clearFocus(payload); diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 2ada24f3e32aa..5a63ac0a0445d 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -34,6 +34,7 @@ import { LIGHT, FileThemeIcon, FolderThemeIcon } from 'vs/platform/theme/common/ import { FileKind } from 'vs/platform/files/common/files'; import { WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; export class CustomTreeViewPanel extends ViewletPanel { @@ -373,7 +374,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { } private onSelection({ payload }: any): void { - if (payload && payload.source === 'api') { + if (payload && (!!payload.didClickOnTwistie || payload.source === 'api')) { return; } const selection: ITreeItem = this.tree.getSelection()[0]; @@ -586,6 +587,10 @@ class TreeController extends WorkbenchTreeController { super({}, configurationService); } + protected shouldToggleExpansion(element: ITreeItem, event: IMouseEvent, origin: string): boolean { + return element.command ? this.isClickOnTwistie(event) : super.shouldToggleExpansion(element, event, origin); + } + public onContextMenu(tree: ITree, node: ITreeItem, event: ContextMenuEvent): boolean { event.preventDefault(); event.stopPropagation(); From f41ee9c0f6a0fdd9e94e385af02d2e9e4d5fb51a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Jun 2018 17:03:31 +0200 Subject: [PATCH 132/149] Stylistic edits --- src/vs/base/common/strings.ts | 28 +----- .../common/controller/cursorWordOperations.ts | 98 ++++++++++--------- .../test/wordPartOperations.test.ts | 2 + 3 files changed, 54 insertions(+), 74 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index c8072e04a3e27..31dff819977ba 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -280,30 +280,6 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len return -1; } -export function lastWordPartEnd(str: string, startIndex: number = str.length - 1): number { - for (let i = startIndex; i >= 0; i--) { - let chCode = str.charCodeAt(i); - if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { - return i - 1; - } - } - return -1; -} - -export function nextWordPartBegin(str: string, startIndex: number = str.length - 1): number { - const checkLowerCase = str.charCodeAt(startIndex - 1) === CharCode.Space; // does a lc char count as a part start? - for (let i = startIndex; i < str.length; ++i) { - let chCode = str.charCodeAt(i); - if (chCode === CharCode.Space || chCode === CharCode.Tab || isUpperAsciiLetter(chCode) || (checkLowerCase && isLowerAsciiLetter(chCode))) { - return i + 1; - } - if (chCode === CharCode.Underline) { - return i + 2; - } - } - return -1; -} - export function compare(a: string, b: string): number { if (a < b) { return -1; @@ -357,11 +333,11 @@ export function compareIgnoreCase(a: string, b: string): number { } } -function isLowerAsciiLetter(code: number): boolean { +export function isLowerAsciiLetter(code: number): boolean { return code >= CharCode.a && code <= CharCode.z; } -function isUpperAsciiLetter(code: number): boolean { +export function isUpperAsciiLetter(code: number): boolean { return code >= CharCode.A && code <= CharCode.Z; } diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 05afe78875e9b..b69d9799b5300 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -10,6 +10,7 @@ import { WordCharacterClassifier, WordCharacterClass, getMapForWordSeparators } import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { CharCode } from 'vs/base/common/charCode'; interface IFindWordResult { /** @@ -464,6 +465,30 @@ export class WordOperations { } } +export function _lastWordPartEnd(str: string, startIndex: number = str.length - 1): number { + for (let i = startIndex; i >= 0; i--) { + let chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || strings.isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { + return i - 1; + } + } + return -1; +} + +export function _nextWordPartBegin(str: string, startIndex: number = str.length - 1): number { + const checkLowerCase = str.charCodeAt(startIndex - 1) === CharCode.Space; // does a lc char count as a part start? + for (let i = startIndex; i < str.length; ++i) { + let chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || strings.isUpperAsciiLetter(chCode) || (checkLowerCase && strings.isLowerAsciiLetter(chCode))) { + return i + 1; + } + if (chCode === CharCode.Underline) { + return i + 2; + } + } + return str.length + 1; +} + export class WordPartOperations extends WordOperations { public static deleteWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { if (!selection.isEmpty()) { @@ -471,9 +496,8 @@ export class WordPartOperations extends WordOperations { } const position = new Position(selection.positionLineNumber, selection.positionColumn); - - let lineNumber = position.lineNumber; - let column = position.column; + const lineNumber = position.lineNumber; + const column = position.column; if (lineNumber === 1 && column === 1) { // Ignore deleting at beginning of file @@ -487,19 +511,14 @@ export class WordPartOperations extends WordOperations { } } - const lineContent = model.getLineContent(position.lineNumber); - const startIndex = position.column - 2; - const lastWordPartEnd = strings.lastWordPartEnd(lineContent, startIndex); const wordRange = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + const lastWordPartEnd = _lastWordPartEnd(model.getLineContent(position.lineNumber), position.column - 2); + const wordPartRange = new Range(lineNumber, column, lineNumber, lastWordPartEnd + 2); - if (lastWordPartEnd === -1 || (wordRange.startColumn > lastWordPartEnd && wordRange.startColumn < column)) { + if (wordPartRange.getStartPosition().isBeforeOrEqual(wordRange.getStartPosition())) { return wordRange; } - else { - const range = new Range(lineNumber, column, lineNumber, lastWordPartEnd + 2); - return range; - } - + return wordPartRange; } public static deleteWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range { @@ -508,9 +527,8 @@ export class WordPartOperations extends WordOperations { } const position = new Position(selection.positionLineNumber, selection.positionColumn); - - let lineNumber = position.lineNumber; - let column = position.column; + const lineNumber = position.lineNumber; + const column = position.column; const lineCount = model.getLineCount(); const maxColumn = model.getLineMaxColumn(lineNumber); @@ -526,64 +544,48 @@ export class WordPartOperations extends WordOperations { } } - const lineContent = model.getLineContent(position.lineNumber); - const startIndex = position.column; - const nextWordPartBegin = strings.nextWordPartBegin(lineContent, startIndex); const wordRange = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType); + const nextWordPartBegin = _nextWordPartBegin(model.getLineContent(position.lineNumber), position.column); + const wordPartRange = new Range(lineNumber, column, lineNumber, nextWordPartBegin); - if (nextWordPartBegin === -1 || (wordRange && wordRange.endColumn < nextWordPartBegin && wordRange.endColumn >= column)) { + if (wordRange.getEndPosition().isBeforeOrEqual(wordPartRange.getEndPosition())) { return wordRange; } - else { - return new Range(lineNumber, column, lineNumber, nextWordPartBegin); - } + return wordPartRange; } public static moveWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { - const startIndex = position.column - 2; const lineNumber = position.lineNumber; const column = position.column; - const wordPartCol = strings.lastWordPartEnd(model.getLineContent(lineNumber), startIndex); - const wordPos = WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType); - if (column === 1) { - if (lineNumber > 1) { - return new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); - } - return null; + return (lineNumber > 1 ? new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)) : position); } - if (wordPartCol === -1 || (wordPos.column > wordPartCol && wordPos.column < column && wordPos.lineNumber === lineNumber)) { + const wordPos = WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType); + const lastWordPartEnd = _lastWordPartEnd(model.getLineContent(lineNumber), column - 2); + const wordPartPos = new Position(lineNumber, lastWordPartEnd + 2); + + if (wordPartPos.isBeforeOrEqual(wordPos)) { return wordPos; } - else { - return new Position(lineNumber, wordPartCol + 2); - } + return wordPartPos; } public static moveWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position { - const startIndex = position.column; const lineNumber = position.lineNumber; const column = position.column; - const wordPartCol = strings.nextWordPartBegin(model.getLineContent(lineNumber), startIndex); - const wordPos = WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType); - - const lineCount = model.getLineCount(); const maxColumn = model.getLineMaxColumn(lineNumber); if (column === maxColumn) { - if (lineNumber < lineCount) { - return new Position(lineNumber + 1, 1); - } - return null; + return (lineNumber < model.getLineCount() ? new Position(lineNumber + 1, 1) : position); } + const wordPos = WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType); + const nextWordPartBegin = _nextWordPartBegin(model.getLineContent(lineNumber), column); + const wordPartPos = new Position(lineNumber, nextWordPartBegin); - if (wordPartCol === -1 || (wordPos.column < wordPartCol && wordPos.column > column && wordPos.lineNumber === lineNumber)) { + if (wordPos.isBeforeOrEqual(wordPartPos)) { return wordPos; } - else { - return new Position(lineNumber, wordPartCol); - } + return wordPartPos; } - } diff --git a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts index e5fa29f253536..2074dd9cfc691 100644 --- a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts +++ b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts @@ -144,10 +144,12 @@ suite('WordPartOperations', () => { deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ ', '013'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */', '014'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 ', '015'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3', '015bis'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-', '016'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5', '017'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '018'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '019'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3', '019bis'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '020'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+=', '021'); deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '022'); From c177376801e6bd477dba4d06022ace306afebf79 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 17:12:59 +0200 Subject: [PATCH 133/149] use posix path math with uris --- .../src/singlefolder-tests/workspace.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index d646e3579423b..1f42a34e5976a 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; import { createRandomFile, deleteFile, closeAllEditors, pathEquals, rndName } from '../utils'; -import { join, basename, dirname } from 'path'; +import { join, posix, basename } from 'path'; import * as fs from 'fs'; import * as os from 'os'; @@ -569,7 +569,7 @@ suite('workspace-namespace', () => { }); function nameWithUnderscore(uri: vscode.Uri) { - return uri.with({ path: join(dirname(uri.path), `_${basename(uri.path)}`) }); + return uri.with({ path: posix.join(posix.dirname(uri.path), `_${posix.basename(uri.path)}`) }); } test('WorkspaceEdit: applying edits before and after rename duplicates resource #42633', async function () { @@ -630,7 +630,7 @@ suite('workspace-namespace', () => { } let docUri = await createRandomFile('', dir); - let docParent = docUri.with({ path: dirname(docUri.path) }); + let docParent = docUri.with({ path: posix.dirname(docUri.path) }); let newParent = nameWithUnderscore(docParent); let we = new vscode.WorkspaceEdit(); @@ -646,7 +646,7 @@ suite('workspace-namespace', () => { assert.ok(true); } - let newUri = newParent.with({ path: join(newParent.path, basename(docUri.path)) }); + let newUri = newParent.with({ path: posix.join(newParent.path, posix.basename(docUri.path)) }); let doc = await vscode.workspace.openTextDocument(newUri); assert.ok(doc); From f22e404cfe78bd1cda97b15678cbd459692d501b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Jun 2018 17:17:05 +0200 Subject: [PATCH 134/149] Add word part selection commands & tweak mac keybindings --- .../wordPartOperations/wordPartOperations.ts | 56 +++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts index 781d1ed588e8b..8ad25d28fa860 100644 --- a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -26,7 +26,7 @@ export class DeleteWordPartLeft extends DeleteWordCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyCode.Backspace, - mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Backspace } } }); } @@ -50,7 +50,7 @@ export class DeleteWordPartRight extends DeleteWordCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyCode.Delete, - mac: { primary: KeyMod.CtrlCmd | KeyCode.Delete } + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Delete } } }); } @@ -66,7 +66,12 @@ export class DeleteWordPartRight extends DeleteWordCommand { } } -export class CursorWordPartLeft extends MoveWordCommand { +export class WordPartLeftCommand extends MoveWordCommand { + protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { + return WordPartOperations.moveWordPartLeft(wordSeparators, model, position, wordNavigationType); + } +} +export class CursorWordPartLeft extends WordPartLeftCommand { constructor() { super({ inSelectionMode: false, @@ -76,17 +81,33 @@ export class CursorWordPartLeft extends MoveWordCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyCode.LeftArrow, - mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.LeftArrow } } }); } +} +export class CursorWordPartLeftSelect extends WordPartLeftCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordStart, + id: 'cursorWordPartStartLeftSelect', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow } + } + }); + } +} +export class WordPartRightCommand extends MoveWordCommand { protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { - return WordPartOperations.moveWordPartLeft(wordSeparators, model, position, wordNavigationType); + return WordPartOperations.moveWordPartRight(wordSeparators, model, position, wordNavigationType); } } - -export class CursorWordPartRight extends MoveWordCommand { +export class CursorWordPartRight extends WordPartRightCommand { constructor() { super({ inSelectionMode: false, @@ -96,13 +117,24 @@ export class CursorWordPartRight extends MoveWordCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.Alt | KeyCode.RightArrow, - mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.RightArrow } } }); } - - protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { - return WordPartOperations.moveWordPartRight(wordSeparators, model, position, wordNavigationType); +} +export class CursorWordPartRightSelect extends WordPartRightCommand { + constructor() { + super({ + inSelectionMode: true, + wordNavigationType: WordNavigationType.WordEnd, + id: 'cursorWordPartRightSelect', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow } + } + }); } } @@ -110,4 +142,6 @@ export class CursorWordPartRight extends MoveWordCommand { registerEditorCommand(new DeleteWordPartLeft()); registerEditorCommand(new DeleteWordPartRight()); registerEditorCommand(new CursorWordPartLeft()); +registerEditorCommand(new CursorWordPartLeftSelect()); registerEditorCommand(new CursorWordPartRight()); +registerEditorCommand(new CursorWordPartRightSelect()); From 23da1518a676069cffacb0535ecf00e29511fd54 Mon Sep 17 00:00:00 2001 From: Christoph Seitz Date: Wed, 20 Jun 2018 17:35:55 +0200 Subject: [PATCH 135/149] Add test for snippet transform with indention. --- .../snippet/test/snippetSession.test.ts | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index ed255770f69a4..8093aa82221b0 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -486,6 +486,46 @@ suite('SnippetSession', function () { assertSelections(editor, new Selection(2, 1, 2, 1)); }); + test('snippets, transform with indent', function () { + const snippet = [ + 'private readonly ${1} = new Emitter<$2>();', + 'readonly ${1/^_(.*)/$1/}: Event<$2> = this.$1.event;', + '$0' + ].join('\n'); + const expected = [ + '{', + '\tprivate readonly _prop = new Emitter();', + '\treadonly prop: Event = this._prop.event;', + '\t', + '}' + ].join('\n'); + const base = [ + '{', + '\t', + '}' + ].join('\n'); + + editor.getModel().setValue(base); + editor.getModel().updateOptions({ insertSpaces: false }); + editor.setSelection(new Selection(2, 2, 2, 2)); + + const session = new SnippetSession(editor, snippet); + session.insert(); + + assertSelections(editor, new Selection(2, 19, 2, 19), new Selection(3, 11, 3, 11), new Selection(3, 28, 3, 28)); + editor.trigger('test', 'type', { text: '_prop' }); + session.next(); + + assertSelections(editor, new Selection(2, 39, 2, 39), new Selection(3, 23, 3, 23)); + editor.trigger('test', 'type', { text: 'string' }); + session.next(); + + assert.equal(model.getValue(), expected); + assert.equal(session.isAtLastPlaceholder, true); + assertSelections(editor, new Selection(4, 2, 4, 2)); + + }); + test('snippets, transform example hit if', function () { editor.getModel().setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); From 11787931988ffeead8071f3fe0f0c7bcff89ef10 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 20 Jun 2018 17:38:14 +0200 Subject: [PATCH 136/149] Disable history navigation because up and down are used to navigate through the suggest widget fixes #52381 --- src/vs/workbench/parts/debug/electron-browser/repl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 9a318ab59e0eb..8574e52a10eb4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -174,6 +174,8 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati modes.SuggestRegistry.register({ scheme: DEBUG_SCHEME, hasAccessToAllModels: true }, { triggerCharacters: ['.'], provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable => { + // Disable history navigation because up and down are used to navigate through the suggest widget + historyNavigationEnablement.set(false); const word = this.replInput.getModel().getWordAtPosition(position); const overwriteBefore = word ? word.word.length : 0; const text = this.replInput.getModel().getLineContent(position.lineNumber); @@ -198,7 +200,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati historyNavigationEnablement.set(this.replInput.getModel().getLineCount() === 1); })); - this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.FOCUS, () => dom.addClass(this.replInputContainer, 'synthetic-focus'))); this.toUnbind.push(dom.addStandardDisposableListener(this.replInputContainer, dom.EventType.BLUR, () => dom.removeClass(this.replInputContainer, 'synthetic-focus'))); } From aee819955f7b9fc4ebe4296377446d60b9dc9cc4 Mon Sep 17 00:00:00 2001 From: Christoph Seitz Date: Wed, 20 Jun 2018 17:41:19 +0200 Subject: [PATCH 137/149] Update documentation. --- src/vs/editor/contrib/snippet/snippet.md | 31 ++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/snippet/snippet.md b/src/vs/editor/contrib/snippet/snippet.md index 403cda7743839..6b29d44c16e0c 100644 --- a/src/vs/editor/contrib/snippet/snippet.md +++ b/src/vs/editor/contrib/snippet/snippet.md @@ -53,6 +53,26 @@ ${TM_FILENAME/(.*)\..+$/$1/} |-> resolves to the filename ``` +Placeholder-Transform +-- + +Like a Variable-Transform, a transformation of a placeholder allows changing the inserted text for the placeholder when moving to the next tab stop. +The inserted text is matched with the regular expression and the match or matches - depending on the options - are replaced with the specified replacement format text. +Every occurrence of a placeholder can define its own transformation independently using the value of the first placeholder. +The format for Placeholder-Transforms is the same as for Variable-Transforms. + +The following sample removes an underscore at the beginning of the text. `_transform` becomes `transform`. + +``` +${1/^_(.*)/$1/} + | | | |-> No options + | | | + | | |-> Replace it with the first capture group + | | + | |-> Regular expression to capture everything after the underscore + | + |-> Placeholder Index +``` Grammar -- @@ -61,12 +81,17 @@ Below is the EBNF for snippets. With `\` (backslash) you can escape `$`, `}` and ``` any ::= tabstop | placeholder | choice | variable | text -tabstop ::= '$' int | '${' int '}' +tabstop ::= '$' int + | '${' int '}' + | '${' int transform '}' placeholder ::= '${' int ':' any '}' + | '${' int ':' any transform '}' choice ::= '${' int '|' text (',' text)* '|}' + | '${' int '|' text (',' text)* '|' transform '}' variable ::= '$' var | '${' var }' | '${' var ':' any '}' - | '${' var '/' regex '/' (format | text)+ '/' options '}' + | '${' var transform '}' +transform ::= '/' regex '/' (format | text)+ '/' options format ::= '$' int | '${' int '}' | '${' int ':' '/upcase' | '/downcase' | '/capitalize' '}' | '${' int ':+' if '}' @@ -78,3 +103,5 @@ var ::= [_a-zA-Z] [_a-zA-Z0-9]* int ::= [0-9]+ text ::= .* ``` + +Transformations for placeholders and choices are an extension to the TextMate snippet grammar and only support by Visual Studio Code. \ No newline at end of file From 66eb17581b3ade75da683c02f936b2355ab89627 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Jun 2018 17:42:32 +0200 Subject: [PATCH 138/149] Tweak keybindings on Windows --- .../contrib/wordPartOperations/wordPartOperations.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts index 8ad25d28fa860..28829ba7b317e 100644 --- a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -25,7 +25,7 @@ export class DeleteWordPartLeft extends DeleteWordCommand { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyCode.Backspace, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Backspace, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Backspace } } }); @@ -49,7 +49,7 @@ export class DeleteWordPartRight extends DeleteWordCommand { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyCode.Delete, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Delete, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Delete } } }); @@ -80,7 +80,7 @@ export class CursorWordPartLeft extends WordPartLeftCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyCode.LeftArrow, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.LeftArrow } } }); @@ -95,7 +95,7 @@ export class CursorWordPartLeftSelect extends WordPartLeftCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow } } }); @@ -116,7 +116,7 @@ export class CursorWordPartRight extends WordPartRightCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyCode.RightArrow, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.RightArrow } } }); @@ -131,7 +131,7 @@ export class CursorWordPartRightSelect extends WordPartRightCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow } } }); From 89f9e25fc758cbab5c5c91d63ee1e3178ca8ab8c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 20 Jun 2018 18:15:30 +0200 Subject: [PATCH 139/149] outline - use better twistie knowledge --- .../contrib/documentSymbols/outlineTree.ts | 33 +++---------------- .../outline/electron-browser/outlinePanel.ts | 2 +- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index a38dcf406aaf5..fa58d75a59fbc 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -9,7 +9,6 @@ import 'vs/css!./media/symbol-icons'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { values } from 'vs/base/common/collections'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { createMatches } from 'vs/base/common/filters'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; @@ -307,35 +306,11 @@ export class OutlineTreeState { } export class OutlineController extends WorkbenchTreeController { - - protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean { - - const payload = { origin: origin, originalEvent: event, didClickElement: false }; - - if (tree.getInput() === element) { - tree.clearFocus(payload); - tree.clearSelection(payload); + protected shouldToggleExpansion(element: any, event: IMouseEvent, origin: string): boolean { + if (element instanceof OutlineElement) { + return this.isClickOnTwistie(event); } else { - const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; - if (!isMouseDown) { - event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise - } - event.stopPropagation(); - - payload.didClickElement = element instanceof OutlineElement && !this.isClickOnTwistie(event); - - tree.domFocus(); - tree.setSelection([element], payload); - tree.setFocus(element, payload); - - if (!payload.didClickElement) { - if (tree.isExpanded(element)) { - tree.collapse(element).then(null, onUnexpectedError); - } else { - tree.expand(element).then(null, onUnexpectedError); - } - } + return super.shouldToggleExpansion(element, event, origin); } - return true; } } diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 52b9944bbb65e..44db10318fba8 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -549,7 +549,7 @@ export class OutlinePanel extends ViewletPanel { // feature: reveal outline selection in editor // on change -> reveal/select defining range this._editorDisposables.push(this._tree.onDidChangeSelection(e => { - if (e.payload === this || e.payload && !e.payload.didClickElement) { + if (e.payload === this || e.payload && e.payload.didClickOnTwistie) { return; } let [first] = e.selection; From 2c0c1f3d66c9d30902391ad28b4ee6b5233ffdad Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Jun 2018 11:10:01 -0700 Subject: [PATCH 140/149] Use ignoreIfExists for creating new files during refactorings --- .../typescript-language-features/src/features/refactor.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 4aa18755c4ce2..5feec27bcfc2c 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -57,11 +57,7 @@ class ApplyRefactoringCommand implements Command { private async toWorkspaceEdit(body: Proto.RefactorEditInfo) { const workspaceEdit = new vscode.WorkspaceEdit(); for (const edit of body.edits) { - try { - await vscode.workspace.openTextDocument(edit.fileName); - } catch { - workspaceEdit.createFile(this.client.toResource(edit.fileName)); - } + workspaceEdit.createFile(this.client.toResource(edit.fileName), { ignoreIfExists: true }); } typeConverters.WorkspaceEdit.withFileCodeEdits(workspaceEdit, this.client, body.edits); return workspaceEdit; From b1361059942191ab6d01536bb4c098e40f38c9ad Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 20 Jun 2018 10:21:04 -0700 Subject: [PATCH 141/149] Settings editor - show result count in TOC when searching --- .../browser/media/settingsEditor2.css | 6 +- .../preferences/browser/settingsEditor2.ts | 24 ++++-- .../parts/preferences/browser/tocTree.ts | 76 +++++++++++++++++-- 3 files changed, 92 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index 3ddb397670ea5..856c4773c472c 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -115,7 +115,7 @@ } .search-mode .settings-toc-container { - display: none; + /* display: none; */ } .settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-entry { @@ -124,6 +124,10 @@ line-height: 22px; } +.settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-entry.no-results { + opacity: 0.5; +} + .settings-editor > .settings-body .settings-tree-container { flex: 1; border-spacing: 0; diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index d05e2d6a2bf4f..4b47bd0ae70f0 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -31,7 +31,7 @@ import { EditorOptions, IEditor } from 'vs/workbench/common/editor'; import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { tocData, commonlyUsedData } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { ISettingsEditorViewState, SearchResultIdx, SearchResultModel, SettingsAccessibilityProvider, SettingsDataSource, SettingsRenderer, SettingsTreeController, SettingsTreeElement, SettingsTreeFilter, SettingsTreeModel, SettingsTreeSettingElement, SettingsTreeGroupElement, resolveSettingsTree, NonExpandableTree } from 'vs/workbench/parts/preferences/browser/settingsTree'; -import { TOCDataSource, TOCRenderer } from 'vs/workbench/parts/preferences/browser/tocTree'; +import { TOCDataSource, TOCRenderer, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences'; import { IPreferencesService, ISearchResult, ISettingsEditorModel } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; @@ -55,6 +55,7 @@ export class SettingsEditor2 extends BaseEditor { private settingsTreeContainer: HTMLElement; private settingsTree: WorkbenchTree; private treeDataSource: SettingsDataSource; + private tocTreeModel: TOCTreeModel; private settingsTreeModel: SettingsTreeModel; private tocTreeContainer: HTMLElement; @@ -226,13 +227,14 @@ export class SettingsEditor2 extends BaseEditor { private createTOC(parent: HTMLElement): void { this.tocTreeContainer = DOM.append(parent, $('.settings-toc-container')); - const tocTreeDataSource = this.instantiationService.createInstance(TOCDataSource); - const renderer = this.instantiationService.createInstance(TOCRenderer); + const tocDataSource = this.instantiationService.createInstance(TOCDataSource); + const tocRenderer = this.instantiationService.createInstance(TOCRenderer); + this.tocTreeModel = new TOCTreeModel(); this.tocTree = this.instantiationService.createInstance(WorkbenchTree, this.tocTreeContainer, { - dataSource: tocTreeDataSource, - renderer, + dataSource: tocDataSource, + renderer: tocRenderer, filter: this.instantiationService.createInstance(SettingsTreeFilter, this.viewState) }, { @@ -480,7 +482,13 @@ export class SettingsEditor2 extends BaseEditor { } else { this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState, resolvedSettingsRoot); this.settingsTree.setInput(this.settingsTreeModel.root); - this.tocTree.setInput(this.settingsTreeModel.root); + + this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement; + if (this.tocTree.getInput()) { + this.tocTree.refresh(); + } else { + this.tocTree.setInput(this.tocTreeModel); + } } return this.refreshTreeAndMaintainFocus(); @@ -538,6 +546,8 @@ export class SettingsEditor2 extends BaseEditor { } this.searchResultModel = null; + this.tocTreeModel.currentSearchModel = null; + this.tocTree.refresh(); this.toggleSearchMode(); this.settingsTree.setInput(this.settingsTreeModel.root); @@ -613,11 +623,13 @@ export class SettingsEditor2 extends BaseEditor { const [result] = results; if (!this.searchResultModel) { this.searchResultModel = new SearchResultModel(); + this.tocTreeModel.currentSearchModel = this.searchResultModel; this.toggleSearchMode(); this.settingsTree.setInput(this.searchResultModel); } this.searchResultModel.setResult(type, result); + this.tocTreeModel.update(); resolve(this.refreshTreeAndMaintainFocus()); }); }, () => { diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index b2b92fe6e7a03..02148d1f21385 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -6,25 +6,82 @@ import * as DOM from 'vs/base/browser/dom'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; -import { SettingsTreeElement, SettingsTreeGroupElement } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { SearchResultModel, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { ISetting } from 'vs/workbench/services/preferences/common/preferences'; const $ = DOM.$; +export class TOCTreeModel { + private _currentSearchModel: SearchResultModel; + private _settingsTreeRoot: SettingsTreeGroupElement; + + public set settingsTreeRoot(value: SettingsTreeGroupElement) { + this._settingsTreeRoot = value; + this.update(); + } + + public set currentSearchModel(model: SearchResultModel) { + this._currentSearchModel = model; + this.update(); + } + + public get children(): SettingsTreeElement[] { + return this._settingsTreeRoot.children; + } + + public update(): void { + this.updateGroupCount(this._settingsTreeRoot); + } + + private updateGroupCount(group: SettingsTreeGroupElement): void { + (group).count = this._currentSearchModel ? + this.getSearchResultChildrenCount(group) : + undefined; + + group.children.forEach(child => { + if (child instanceof SettingsTreeGroupElement) { + this.updateGroupCount(child); + } + }); + } + + private getSearchResultChildrenCount(group: SettingsTreeGroupElement): number { + return this._currentSearchModel.getFlatSettings().filter(s => { + return this.groupContainsSetting(group, s); + }).length; + } + + private groupContainsSetting(group: SettingsTreeGroupElement, setting: ISetting): boolean { + return group.children.some(child => { + if (child instanceof SettingsTreeSettingElement) { + return child.setting.key === setting.key; + } else if (child instanceof SettingsTreeGroupElement) { + return this.groupContainsSetting(child, setting); + } else { + return false; + } + }); + } +} + +export type TOCTreeElement = SettingsTreeGroupElement | TOCTreeModel; + export class TOCDataSource implements IDataSource { getId(tree: ITree, element: SettingsTreeGroupElement): string { return element.id; } - hasChildren(tree: ITree, element: SettingsTreeElement): boolean { - return element instanceof SettingsTreeGroupElement && element.children && element.children.every(child => child instanceof SettingsTreeGroupElement); + hasChildren(tree: ITree, element: TOCTreeElement): boolean { + return element instanceof TOCTreeModel || + (element instanceof SettingsTreeGroupElement && element.children && element.children.every(child => child instanceof SettingsTreeGroupElement)); } - getChildren(tree: ITree, element: SettingsTreeGroupElement): TPromise { + getChildren(tree: ITree, element: TOCTreeElement): TPromise { return TPromise.as(element.children); } - getParent(tree: ITree, element: SettingsTreeElement): TPromise { - return TPromise.wrap(element.parent); + getParent(tree: ITree, element: TOCTreeElement): TPromise { + return TPromise.wrap(element instanceof SettingsTreeGroupElement && element.parent); } shouldAutoexpand() { @@ -54,7 +111,12 @@ export class TOCRenderer implements IRenderer { } renderElement(tree: ITree, element: SettingsTreeGroupElement, templateId: string, template: ITOCEntryTemplate): void { - template.element.textContent = element.label; + const label = (element).count ? + `${element.label} (${(element).count})` : + element.label; + + DOM.toggleClass(template.element, 'no-results', (element).count === 0); + template.element.textContent = label; } disposeTemplate(tree: ITree, templateId: string, templateData: any): void { From b31c338cfee2ff2f6dc58f173d597a5a271b6213 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 20 Jun 2018 10:54:07 -0700 Subject: [PATCH 142/149] Settings editor - add setting to configure TOC behavior during search --- .../electron-browser/main.contribution.ts | 6 ++++ .../browser/media/settingsEditor2.css | 3 +- .../preferences/browser/settingsEditor2.ts | 5 +++- .../parts/preferences/browser/tocTree.ts | 30 +++++++++++++++++-- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 8ff4d6a55bf2e..b5b9b6e9ba168 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -306,6 +306,12 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings."), 'default': true + }, + 'workbench.settings.settingsSearchTocBehavior': { + 'type': 'string', + 'enum': ['hide', 'filter', 'show'], + 'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the settings editor TOC while searching."), + 'default': 'hide' } } }); diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index 856c4773c472c..67068eb9e6c64 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -101,7 +101,6 @@ .settings-editor > .settings-body { display: flex; margin: auto; - margin-top: 5px; max-width: 1000px; } @@ -115,7 +114,7 @@ } .search-mode .settings-toc-container { - /* display: none; */ + display: none; } .settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-entry { diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index 4b47bd0ae70f0..826394a18ef8c 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -468,7 +468,10 @@ export class SettingsEditor2 extends BaseEditor { } private toggleSearchMode(): void { - DOM.toggleClass(this.rootElement, 'search-mode', !!this.searchResultModel); + DOM.removeClass(this.rootElement, 'search-mode'); + if (this.configurationService.getValue('workbench.settings.settingsSearchTocBehavior') === 'hide') { + DOM.toggleClass(this.rootElement, 'search-mode', !!this.searchResultModel); + } } private onConfigUpdate(): TPromise { diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index 02148d1f21385..4892f4de1efc9 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -8,10 +8,12 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; import { SearchResultModel, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTree'; import { ISetting } from 'vs/workbench/services/preferences/common/preferences'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const $ = DOM.$; export class TOCTreeModel { + private _currentSearchModel: SearchResultModel; private _settingsTreeRoot: SettingsTreeGroupElement; @@ -67,6 +69,11 @@ export class TOCTreeModel { export type TOCTreeElement = SettingsTreeGroupElement | TOCTreeModel; export class TOCDataSource implements IDataSource { + constructor( + @IConfigurationService private configService: IConfigurationService + ) { + } + getId(tree: ITree, element: SettingsTreeGroupElement): string { return element.id; } @@ -77,7 +84,18 @@ export class TOCDataSource implements IDataSource { } getChildren(tree: ITree, element: TOCTreeElement): TPromise { - return TPromise.as(element.children); + return TPromise.as(this._getChildren(element)); + } + + private _getChildren(element: TOCTreeElement): SettingsTreeElement[] { + if (this.configService.getValue('workbench.settings.settingsSearchTocBehavior') === 'filter') { + const children = element.children as SettingsTreeElement[]; // ???? + return children.filter(group => { + return (group).count !== 0; + }); + } + + return element.children; } getParent(tree: ITree, element: TOCTreeElement): TPromise { @@ -96,6 +114,11 @@ interface ITOCEntryTemplate { } export class TOCRenderer implements IRenderer { + constructor( + @IConfigurationService private configService: IConfigurationService + ) { + } + getHeight(tree: ITree, element: SettingsTreeElement): number { return 22; } @@ -115,7 +138,10 @@ export class TOCRenderer implements IRenderer { `${element.label} (${(element).count})` : element.label; - DOM.toggleClass(template.element, 'no-results', (element).count === 0); + if (this.configService.getValue('workbench.settings.settingsSearchTocBehavior') === 'show') { + DOM.toggleClass(template.element, 'no-results', (element).count === 0); + } + template.element.textContent = label; } From 94edf19bf370c9e71ce2f3cd9330a363f1a5b46a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 20 Jun 2018 11:36:39 -0700 Subject: [PATCH 143/149] Settings editor - filter result list when a TOC entry is clicked during search --- .../preferences/browser/settingsEditor2.ts | 7 ++++++- .../parts/preferences/browser/settingsTree.ts | 21 ++++++++++++++++++- .../parts/preferences/browser/tocTree.ts | 10 +-------- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index 826394a18ef8c..c024a63eb7ff0 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -243,7 +243,11 @@ export class SettingsEditor2 extends BaseEditor { }); this._register(this.tocTree.onDidChangeSelection(e => { - if (this.settingsTreeModel) { + if (this.searchResultModel) { + const element = e.selection[0]; + this.viewState.filterToCategory = element; + this.refreshTreeAndMaintainFocus(); + } else if (this.settingsTreeModel) { const element = e.selection[0]; const currentSelection = this.settingsTree.getSelection()[0]; const isEqualOrParent = (element: SettingsTreeElement, candidate: SettingsTreeElement) => { @@ -550,6 +554,7 @@ export class SettingsEditor2 extends BaseEditor { this.searchResultModel = null; this.tocTreeModel.currentSearchModel = null; + this.viewState.filterToCategory = null; this.tocTree.refresh(); this.toggleSearchMode(); this.settingsTree.setInput(this.settingsTreeModel.root); diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 57e2808f647c0..0802bb38bb540 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -359,6 +359,7 @@ function trimCategoryForGroup(category: string, groupId: string): string { export interface ISettingsEditorViewState { settingsTarget: SettingsTarget; showConfiguredOnly?: boolean; + filterToCategory?: SettingsTreeGroupElement; } interface IDisposableTemplate { @@ -674,7 +675,13 @@ export class SettingsTreeFilter implements IFilter { ) { } isVisible(tree: ITree, element: SettingsTreeElement): boolean { - if (this.viewState.showConfiguredOnly && element instanceof SettingsTreeSettingElement) { + if (this.viewState.filterToCategory && element instanceof SettingsTreeSettingElement) { + if (!this.settingContainedInGroup(element.setting, this.viewState.filterToCategory)) { + return false; + } + } + + if (element instanceof SettingsTreeSettingElement && this.viewState.showConfiguredOnly) { return element.isConfigured; } @@ -685,6 +692,18 @@ export class SettingsTreeFilter implements IFilter { return true; } + private settingContainedInGroup(setting: ISetting, group: SettingsTreeGroupElement): boolean { + return group.children.some(child => { + if (child instanceof SettingsTreeGroupElement) { + return this.settingContainedInGroup(setting, child); + } else if (child instanceof SettingsTreeSettingElement) { + return child.setting.key === setting.key; + } else { + return false; + } + }); + } + private groupHasConfiguredSetting(element: SettingsTreeGroupElement): boolean { for (let child of element.children) { if (child instanceof SettingsTreeSettingElement) { diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index 4892f4de1efc9..e7e175e1661a2 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -114,11 +114,6 @@ interface ITOCEntryTemplate { } export class TOCRenderer implements IRenderer { - constructor( - @IConfigurationService private configService: IConfigurationService - ) { - } - getHeight(tree: ITree, element: SettingsTreeElement): number { return 22; } @@ -138,10 +133,7 @@ export class TOCRenderer implements IRenderer { `${element.label} (${(element).count})` : element.label; - if (this.configService.getValue('workbench.settings.settingsSearchTocBehavior') === 'show') { - DOM.toggleClass(template.element, 'no-results', (element).count === 0); - } - + DOM.toggleClass(template.element, 'no-results', (element).count === 0); template.element.textContent = label; } From 04c1ad98b3e574d3dd68b13bdba6f29c306f84c8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 20 Jun 2018 11:49:03 -0700 Subject: [PATCH 144/149] EH search - Centralize vscode-ripgrep import in search extension --- extensions/search-rg/src/ripgrep.ts | 10 ++++++++++ extensions/search-rg/src/ripgrepFileSearch.ts | 2 +- extensions/search-rg/src/ripgrepTextSearch.ts | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 extensions/search-rg/src/ripgrep.ts diff --git a/extensions/search-rg/src/ripgrep.ts b/extensions/search-rg/src/ripgrep.ts new file mode 100644 index 0000000000000..cb3878ee03177 --- /dev/null +++ b/extensions/search-rg/src/ripgrep.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { rgPath } from 'vscode-ripgrep'; + +export { rgPath }; \ No newline at end of file diff --git a/extensions/search-rg/src/ripgrepFileSearch.ts b/extensions/search-rg/src/ripgrepFileSearch.ts index d1c5618b7230e..eaa75d647efdb 100644 --- a/extensions/search-rg/src/ripgrepFileSearch.ts +++ b/extensions/search-rg/src/ripgrepFileSearch.ts @@ -7,8 +7,8 @@ import * as cp from 'child_process'; import { Readable } from 'stream'; import { NodeStringDecoder, StringDecoder } from 'string_decoder'; import * as vscode from 'vscode'; -import { rgPath } from 'vscode-ripgrep'; import { normalizeNFC, normalizeNFD } from './normalization'; +import { rgPath } from './ripgrep'; import { anchorGlob } from './ripgrepHelpers'; import { rgErrorMsgForDisplay } from './ripgrepTextSearch'; diff --git a/extensions/search-rg/src/ripgrepTextSearch.ts b/extensions/search-rg/src/ripgrepTextSearch.ts index 08705d11213cc..b03ee60c7549a 100644 --- a/extensions/search-rg/src/ripgrepTextSearch.ts +++ b/extensions/search-rg/src/ripgrepTextSearch.ts @@ -9,7 +9,7 @@ import * as cp from 'child_process'; import { EventEmitter } from 'events'; import { NodeStringDecoder, StringDecoder } from 'string_decoder'; import * as vscode from 'vscode'; -import { rgPath } from 'vscode-ripgrep'; +import { rgPath } from './ripgrep'; import { anchorGlob } from './ripgrepHelpers'; // If vscode-ripgrep is in an .asar file, then the binary is unpacked. From 0532c31e4c1eee343aec19b55672c2d79b51f6f4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Jun 2018 11:52:47 -0700 Subject: [PATCH 145/149] Definition link API (#52230) * Definition link Add a new `DefinitionLink` type. This type allows definition providers to return additional metadata about a definition, such as the defining span. Hook up this new provider for typescript This PR replaces #48001 * Correctly mark field optional * Small code fixes - Use lift - Remove unused param * Adding documentation --- .../src/features/definitionProviderBase.ts | 2 +- .../src/features/definitions.ts | 50 ++++++++++++++++++- .../src/typescriptService.ts | 1 + src/vs/editor/common/modes.ts | 9 +++- .../contrib/goToDefinition/goToDefinition.ts | 8 +-- .../goToDefinition/goToDefinitionMouse.ts | 15 ++++-- src/vs/monaco.d.ts | 9 +++- src/vs/vscode.proposed.d.ts | 43 ++++++++++++++++ .../mainThreadLanguageFeatures.ts | 20 ++++++-- src/vs/workbench/api/node/extHost.protocol.ts | 9 +++- .../api/node/extHostLanguageFeatures.ts | 25 ++++++++-- 11 files changed, 169 insertions(+), 22 deletions(-) diff --git a/extensions/typescript-language-features/src/features/definitionProviderBase.ts b/extensions/typescript-language-features/src/features/definitionProviderBase.ts index c34d6df338260..88de57127484a 100644 --- a/extensions/typescript-language-features/src/features/definitionProviderBase.ts +++ b/extensions/typescript-language-features/src/features/definitionProviderBase.ts @@ -11,7 +11,7 @@ import * as typeConverters from '../utils/typeConverters'; export default class TypeScriptDefinitionProviderBase { constructor( - private readonly client: ITypeScriptServiceClient + protected readonly client: ITypeScriptServiceClient ) { } protected async getSymbolLocations( diff --git a/extensions/typescript-language-features/src/features/definitions.ts b/extensions/typescript-language-features/src/features/definitions.ts index 0f5e565390aec..9ff840cdab482 100644 --- a/extensions/typescript-language-features/src/features/definitions.ts +++ b/extensions/typescript-language-features/src/features/definitions.ts @@ -4,11 +4,57 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import * as typeConverters from '../utils/typeConverters'; import DefinitionProviderBase from './definitionProviderBase'; -class TypeScriptDefinitionProvider extends DefinitionProviderBase implements vscode.DefinitionProvider { - public provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken | boolean): Promise { +export default class TypeScriptDefinitionProvider extends DefinitionProviderBase implements vscode.DefinitionProvider { + constructor( + client: ITypeScriptServiceClient + ) { + super(client); + } + + public async provideDefinition() { + // Implemented by provideDefinition2 + return undefined; + } + + public async provideDefinition2( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken | boolean + ): Promise { + if (this.client.apiVersion.gte(API.v270)) { + const filepath = this.client.toPath(document.uri); + if (!filepath) { + return undefined; + } + + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + try { + const response = await this.client.execute('definitionAndBoundSpan', args, token); + const locations: Proto.FileSpan[] = (response && response.body && response.body.definitions) || []; + if (!locations) { + return undefined; + } + + const span = response.body.textSpan ? typeConverters.Range.fromTextSpan(response.body.textSpan) : undefined; + return locations + .map(location => { + const loc = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); + return { + origin: span, + ...loc, + }; + }); + } catch { + return []; + } + } + return this.getSymbolLocations('definition', document, position, token); } } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 8c5b240f2acb1..67be9e314c06d 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -59,6 +59,7 @@ export interface ITypeScriptServiceClient { execute(command: 'completionEntryDetails', args: Proto.CompletionDetailsRequestArgs, token?: CancellationToken): Promise; execute(command: 'signatureHelp', args: Proto.SignatureHelpRequestArgs, token?: CancellationToken): Promise; execute(command: 'definition', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; + execute(command: 'definitionAndBoundSpan', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command: 'implementation', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command: 'typeDefinition', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command: 'references', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index ac7a7e3e5796f..7905ccba0f472 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -538,6 +538,13 @@ export interface Location { */ export type Definition = Location | Location[]; +export interface DefinitionLink { + origin?: IRange; + uri: URI; + range: IRange; + selectionRange?: IRange; +} + /** * The definition provider interface defines the contract between extensions and * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) @@ -547,7 +554,7 @@ export interface DefinitionProvider { /** * Provide the definition of the symbol at the given position and document. */ - provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; } /** diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts index f79912d832315..eee248b2cb031 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel } from 'vs/editor/common/model'; import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; -import { DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, Location } from 'vs/editor/common/modes'; +import { DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, Location, DefinitionLink } from 'vs/editor/common/modes'; import { CancellationToken } from 'vs/base/common/cancellation'; import { asWinJsPromise } from 'vs/base/common/async'; import { Position } from 'vs/editor/common/core/position'; @@ -21,11 +21,11 @@ function getDefinitions( position: Position, registry: LanguageFeatureRegistry, provide: (provider: T, model: ITextModel, position: Position, token: CancellationToken) => Location | Location[] | Thenable -): TPromise { +): TPromise { const provider = registry.ordered(model); // get results - const promises = provider.map((provider, idx): TPromise => { + const promises = provider.map((provider): TPromise => { return asWinJsPromise((token) => { return provide(provider, model, position, token); }).then(undefined, err => { @@ -39,7 +39,7 @@ function getDefinitions( } -export function getDefinitionsAtPosition(model: ITextModel, position: Position): TPromise { +export function getDefinitionsAtPosition(model: ITextModel, position: Position): TPromise { return getDefinitions(model, position, DefinitionProviderRegistry, (provider, model, position, token) => { return provider.provideDefinition(model, position, token); }); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 13659526cab8d..eb59fca07cebc 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -14,7 +14,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IModeService } from 'vs/editor/common/services/modeService'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { Location, DefinitionProviderRegistry } from 'vs/editor/common/modes'; +import { DefinitionProviderRegistry, DefinitionLink } from 'vs/editor/common/modes'; import { ICodeEditor, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { getDefinitionsAtPosition } from './goToDefinition'; @@ -102,7 +102,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC this.throttler.queue(() => { return state.validate(this.editor) ? this.findDefinition(mouseEvent.target) - : TPromise.wrap(null); + : TPromise.wrap(null); }).then(results => { if (!results || !results.length || !state.validate(this.editor)) { @@ -157,8 +157,15 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC const previewRange = new Range(startLineNumber, 1, endLineNumber + 1, 1); const value = textEditorModel.getValueInRange(previewRange).replace(new RegExp(`^\\s{${minIndent - 1}}`, 'gm'), '').trim(); + let wordRange: Range; + if (result.origin) { + wordRange = Range.lift(result.origin); + } else { + wordRange = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); + } + this.addDecoration( - new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn), + wordRange, new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath), value) ); ref.dispose(); @@ -194,7 +201,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC DefinitionProviderRegistry.has(this.editor.getModel()); } - private findDefinition(target: IMouseTarget): TPromise { + private findDefinition(target: IMouseTarget): TPromise { const model = this.editor.getModel(); if (!model) { return TPromise.as(null); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index fbd261ef7ef6b..3f50cc1a7fea8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4805,6 +4805,13 @@ declare namespace monaco.languages { */ export type Definition = Location | Location[]; + export interface DefinitionLink { + origin?: IRange; + uri: Uri; + range: IRange; + selectionRange?: IRange; + } + /** * The definition provider interface defines the contract between extensions and * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) @@ -4814,7 +4821,7 @@ declare namespace monaco.languages { /** * Provide the definition of the symbol at the given position and document. */ - provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f90fa8bf930ac..94c9f3f88fd6b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -727,4 +727,47 @@ declare module 'vscode' { } //#endregion + + //#region Matt: Deinition range + + /** + * Information about where a symbol is defined. + * + * Provides additional metadata over normal [location](#Location) definitions, including the range of + * the defining symbol + */ + export interface DefinitionLink { + /** + * Span of the symbol being defined in the source file. + * + * Used as the underlined span for mouse definition hover. Defaults to the word range at + * the definition position. + */ + origin?: Range; + + /** + * The resource identifier of the definition. + */ + uri: Uri; + + /** + * The full range of the definition. + * + * For a class definition for example, this would be the entire body of the class definition. + */ + range: Range; + + /** + * The span of the symbol definition. + * + * For a class definition, this would be the class name itself in the class definition. + */ + selectionRange?: Range; + } + + export interface DefinitionProvider { + provideDefinition2?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + //#endregion } diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index ba2cef0387a26..7ecf37c7311c6 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -14,7 +14,7 @@ import { wireCancellationToken } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Position as EditorPosition } from 'vs/editor/common/core/position'; import { Range as EditorRange } from 'vs/editor/common/core/range'; -import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter } from '../node/extHost.protocol'; +import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto } from '../node/extHost.protocol'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; import { IHeapService } from './mainThreadHeapService'; @@ -72,6 +72,20 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha } } + private static _reviveDefinitionLinkDto(data: DefinitionLinkDto): modes.DefinitionLink; + private static _reviveDefinitionLinkDto(data: DefinitionLinkDto[]): modes.DefinitionLink[]; + private static _reviveDefinitionLinkDto(data: DefinitionLinkDto | DefinitionLinkDto[]): modes.DefinitionLink | modes.DefinitionLink[] { + if (!data) { + return data; + } else if (Array.isArray(data)) { + data.forEach(l => MainThreadLanguageFeatures._reviveDefinitionLinkDto(l)); + return data; + } else { + data.uri = URI.revive(data.uri); + return data; + } + } + private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto): search.IWorkspaceSymbol; private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto[]): search.IWorkspaceSymbol[]; private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto | WorkspaceSymbolDto[]): search.IWorkspaceSymbol | search.IWorkspaceSymbol[] { @@ -139,8 +153,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerDeclaractionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.DefinitionProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { - provideDefinition: (model, position, token): Thenable => { - return wireCancellationToken(token, this._proxy.$provideDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto); + provideDefinition: (model, position, token): Thenable => { + return wireCancellationToken(token, this._proxy.$provideDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); } }); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index b5a5b3d993314..f1b585d490bbc 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -752,6 +752,13 @@ export interface LocationDto { range: IRange; } +export interface DefinitionLinkDto { + origin?: IRange; + uri: UriComponents; + range: IRange; + selectionRange?: IRange; +} + export interface WorkspaceSymbolDto extends IdObject { name: string; containerName?: string; @@ -808,7 +815,7 @@ export interface ExtHostLanguageFeaturesShape { $provideDocumentSymbols(handle: number, resource: UriComponents): TPromise; $provideCodeLenses(handle: number, resource: UriComponents): TPromise; $resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol): TPromise; - $provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; + $provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideHover(handle: number, resource: UriComponents, position: IPosition): TPromise; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 83f75db9b2557..f562cb6d6feea 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -150,18 +150,33 @@ class DefinitionAdapter { private readonly _provider: vscode.DefinitionProvider ) { } - provideDefinition(resource: URI, position: IPosition): TPromise { + provideDefinition(resource: URI, position: IPosition): TPromise { let doc = this._documents.getDocumentData(resource).document; let pos = typeConvert.Position.to(position); - return asWinJsPromise(token => this._provider.provideDefinition(doc, pos, token)).then(value => { + + return asWinJsPromise(token => this._provider.provideDefinition2 ? this._provider.provideDefinition2(doc, pos, token) : this._provider.provideDefinition(doc, pos, token)).then((value): modes.DefinitionLink[] => { if (Array.isArray(value)) { - return value.map(typeConvert.location.from); + return (value as (vscode.DefinitionLink | vscode.Location)[]).map(x => DefinitionAdapter.convertDefinitionLink(x)); } else if (value) { - return typeConvert.location.from(value); + return [DefinitionAdapter.convertDefinitionLink(value)]; } return undefined; }); } + + private static convertDefinitionLink(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink { + const definitionLink = value; + return { + origin: definitionLink.origin + ? typeConvert.Range.from(definitionLink.origin) + : undefined, + uri: value.uri, + range: typeConvert.Range.from(value.range), + selectionRange: definitionLink.selectionRange + ? typeConvert.Range.from(definitionLink.selectionRange) + : undefined, + }; + } } class ImplementationAdapter { @@ -974,7 +989,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise { + $provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise { return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position)); } From 98fb7aa305195f95f5b96cf0ca38fe40db7fb97e Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 20 Jun 2018 12:09:57 -0700 Subject: [PATCH 146/149] fixes #52376 --- src/vs/workbench/electron-browser/workbench.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index b53a4c8f1d035..a172152be3be6 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -1188,7 +1188,7 @@ export class Workbench extends Disposable implements IPartService { case Parts.TITLEBAR_PART: return this.getCustomTitleBarStyle() === 'custom' && !browser.isFullscreen(); case Parts.MENUBAR_PART: - return this.isVisible(Parts.TITLEBAR_PART) && !this.menubarHidden; + return !isMacintosh && this.isVisible(Parts.TITLEBAR_PART) && !this.menubarHidden; case Parts.SIDEBAR_PART: return !this.sideBarHidden; case Parts.PANEL_PART: From 1730517d41783329a6197d5efbd871acfb6c3980 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Jun 2018 11:57:20 -0700 Subject: [PATCH 147/149] Flatted out abstract class with only one impl --- .../linesOperations/linesOperations.ts | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index d6d79bee280b2..da8080dd5649f 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -235,8 +235,36 @@ interface IDeleteLinesOperation { positionColumn: number; } -abstract class AbstractRemoveLinesAction extends EditorAction { - _getLinesToRemove(editor: ICodeEditor): IDeleteLinesOperation[] { +class DeleteLinesAction extends EditorAction { + + constructor() { + super({ + id: 'editor.action.deleteLines', + label: nls.localize('lines.delete', "Delete Line"), + alias: 'Delete Line', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textInputFocus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_K + } + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + + let ops = this._getLinesToRemove(editor); + + // Finally, construct the delete lines commands + let commands: ICommand[] = ops.map((op) => { + return new DeleteLinesCommand(op.startLineNumber, op.endLineNumber, op.positionColumn); + }); + + editor.pushUndoStop(); + editor.executeCommands(this.id, commands); + editor.pushUndoStop(); + } + + private _getLinesToRemove(editor: ICodeEditor): IDeleteLinesOperation[] { // Construct delete operations let operations: IDeleteLinesOperation[] = editor.getSelections().map((s) => { @@ -277,36 +305,6 @@ abstract class AbstractRemoveLinesAction extends EditorAction { } } -class DeleteLinesAction extends AbstractRemoveLinesAction { - - constructor() { - super({ - id: 'editor.action.deleteLines', - label: nls.localize('lines.delete', "Delete Line"), - alias: 'Delete Line', - precondition: EditorContextKeys.writable, - kbOpts: { - kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_K - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - - let ops = this._getLinesToRemove(editor); - - // Finally, construct the delete lines commands - let commands: ICommand[] = ops.map((op) => { - return new DeleteLinesCommand(op.startLineNumber, op.endLineNumber, op.positionColumn); - }); - - editor.pushUndoStop(); - editor.executeCommands(this.id, commands); - editor.pushUndoStop(); - } -} - export class IndentLinesAction extends EditorAction { constructor() { super({ From c2e24c00daab1cf91e1aaefcbac6c2ec6c3defdf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Jun 2018 11:59:25 -0700 Subject: [PATCH 148/149] Make unused params with _ --- .../linesOperations/linesOperations.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index da8080dd5649f..7f2c9fb66c44a 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -35,7 +35,7 @@ abstract class AbstractCopyLinesAction extends EditorAction { this.down = down; } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let commands: ICommand[] = []; let selections = editor.getSelections(); @@ -93,7 +93,7 @@ abstract class AbstractMoveLinesAction extends EditorAction { this.down = down; } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let commands: ICommand[] = []; let selections = editor.getSelections(); @@ -149,7 +149,7 @@ export abstract class AbstractSortLinesAction extends EditorAction { this.descending = descending; } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { const selections = editor.getSelections(); for (let i = 0, len = selections.length; i < len; i++) { @@ -209,7 +209,7 @@ export class TrimTrailingWhitespaceAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { let cursors: Position[] = []; if (args.reason === 'auto-save') { @@ -250,7 +250,7 @@ class DeleteLinesAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let ops = this._getLinesToRemove(editor); @@ -319,7 +319,7 @@ export class IndentLinesAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { editor.pushUndoStop(); editor.executeCommands(this.id, TypeOperations.indent(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); editor.pushUndoStop(); @@ -340,7 +340,7 @@ class OutdentLinesAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { CoreEditingCommands.Outdent.runEditorCommand(null, editor, null); } } @@ -359,7 +359,7 @@ export class InsertLineBeforeAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { editor.pushUndoStop(); editor.executeCommands(this.id, TypeOperations.lineInsertBefore(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); } @@ -379,14 +379,14 @@ export class InsertLineAfterAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { editor.pushUndoStop(); editor.executeCommands(this.id, TypeOperations.lineInsertAfter(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections())); } } export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction { - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { const primaryCursor = editor.getSelection(); let rangesToDelete = this._getRangesToDelete(editor); // merge overlapping selections @@ -564,7 +564,7 @@ export class JoinLinesAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let selections = editor.getSelections(); let primaryCursor = editor.getSelection(); @@ -707,7 +707,7 @@ export class TransposeAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let selections = editor.getSelections(); let model = editor.getModel(); let commands: ICommand[] = []; @@ -748,7 +748,7 @@ export class TransposeAction extends EditorAction { } export abstract class AbstractCaseAction extends EditorAction { - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let selections = editor.getSelections(); let model = editor.getModel(); let commands: ICommand[] = []; From 30cd45c6c7c502556ec0613d4c9ef16f790d75ca Mon Sep 17 00:00:00 2001 From: bitshiftza <33934381+bitshiftza@users.noreply.github.com> Date: Wed, 20 Jun 2018 21:53:45 +0200 Subject: [PATCH 149/149] Improved alorithm for bracket based previews --- src/vs/editor/common/model.ts | 4 +- .../goToDefinition/goToDefinitionMouse.ts | 119 ++++++++++++++---- 2 files changed, 94 insertions(+), 29 deletions(-) diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index e4a6ba3feedcb..2d33f3731f1eb 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -828,8 +828,8 @@ export interface ITextModel { getWordUntilPosition(position: IPosition): IWordAtPosition; /** - * Find the matching bracket of `request` up, counting brackets. - * @param request The bracket we're searching for + * Find the matching bracket of `bracket` up, counting brackets. + * @param bracket The bracket we're searching for * @param position The position at which to start the search. * @return The range of the matching bracket, or null if the bracket match was not found. * @internal diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 774d502bdc98c..0ac6c710098ec 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -25,7 +25,8 @@ import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegist import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; import { DefinitionAction, DefinitionActionConfig } from './goToDefinitionCommands'; import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture'; -import { IWordAtPosition, IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IWordAtPosition, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { Position } from 'vs/editor/common/core/position'; class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution { @@ -141,35 +142,11 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC return; } - const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber); - const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES); - let endLineNumber = startLineNumber + 1; - let minIndent = startIndent; - - for (; endLineNumber < maxLineNumber; endLineNumber++) { - let endIndent = textEditorModel.getLineFirstNonWhitespaceColumn(endLineNumber); - minIndent = Math.min(minIndent, endIndent); - - // Fix for https://github.com/Microsoft/vscode/issues/39458 - const rangeToTestForOpeningBracket = new Range(endLineNumber, 1, endLineNumber + 1, 1); - const contentWithPotentialOpeningBracket = textEditorModel.getValueInRange(rangeToTestForOpeningBracket) - .replace(new RegExp(`^\\s{${minIndent - 1}}`, 'gm'), '').trim(); - - if (contentWithPotentialOpeningBracket.length > 0 && contentWithPotentialOpeningBracket[0] === '{') { - continue; - } - - if (startIndent === endIndent) { - break; - } - } - - const previewRange = new Range(startLineNumber, 1, endLineNumber + 1, 1); - const value = textEditorModel.getValueInRange(previewRange).replace(new RegExp(`^\\s{${minIndent - 1}}`, 'gm'), '').trim(); + const previewValue = this.getPreviewValue(textEditorModel, startLineNumber); this.addDecoration( new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn), - new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath), value) + new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath), previewValue) ); ref.dispose(); }); @@ -177,6 +154,94 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC }).done(undefined, onUnexpectedError); } + private getPreviewValue(textEditorModel: ITextModel, startLineNumber: number) { + let rangeToUse = this.getPreviewRangeBasedOnIndentation(textEditorModel, startLineNumber); + const numberOfLinesInRange = rangeToUse.endLineNumber - rangeToUse.startLineNumber; + if (numberOfLinesInRange < 3 || numberOfLinesInRange >= GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES) { + rangeToUse = this.getPreviewRangeBasedOnBrackets(textEditorModel, startLineNumber); + } + + const previewValue = this.stripIndentationFromPreviewRange(textEditorModel, startLineNumber, rangeToUse); + return previewValue; + } + + private stripIndentationFromPreviewRange(textEditorModel: ITextModel, startLineNumber: number, previewRange: Range) { + const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber); + let minIndent = startIndent; + + for (let endLineNumber = startLineNumber + 1; endLineNumber < previewRange.endLineNumber; endLineNumber++) { + const endIndent = textEditorModel.getLineFirstNonWhitespaceColumn(endLineNumber); + minIndent = Math.min(minIndent, endIndent); + } + + const previewValue = textEditorModel.getValueInRange(previewRange).replace(new RegExp(`^\\s{${minIndent - 1}}`, 'gm'), '').trim(); + return previewValue; + } + + private getPreviewRangeBasedOnIndentation(textEditorModel: ITextModel, startLineNumber: number) { + const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber); + const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES); + let endLineNumber = startLineNumber + 1; + + for (; endLineNumber < maxLineNumber; endLineNumber++) { + let endIndent = textEditorModel.getLineFirstNonWhitespaceColumn(endLineNumber); + + if (startIndent === endIndent) { + break; + } + } + + const previewRange = new Range(startLineNumber, 1, endLineNumber + 1, 1); + + return previewRange; + } + + private getPreviewRangeBasedOnBrackets(textEditorModel: ITextModel, startLineNumber: number) { + const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES); + + const brackets = []; + + let ignoreFirstEmpty = true; + let currentBracket = textEditorModel.findNextBracket(new Position(startLineNumber, 1)); + while (currentBracket !== null) { + + if (brackets.length === 0) { + brackets.push(currentBracket); + } else { + const lastBracket = brackets[brackets.length - 1]; + if (lastBracket.open === currentBracket.open && lastBracket.isOpen && !currentBracket.isOpen) { + brackets.pop(); + } else { + brackets.push(currentBracket); + } + + if (brackets.length === 0) { + if (ignoreFirstEmpty) { + ignoreFirstEmpty = false; + } else { + return new Range(startLineNumber, 1, currentBracket.range.endLineNumber + 1, 1); + } + } + } + + const maxColumn = textEditorModel.getLineMaxColumn(startLineNumber); + let nextLineNumber = currentBracket.range.endLineNumber; + let nextColumn = currentBracket.range.endColumn; + if (maxColumn === currentBracket.range.endColumn) { + nextLineNumber++; + nextColumn = 1; + } + + if (nextLineNumber > maxLineNumber) { + return new Range(startLineNumber, 1, maxLineNumber + 1, 1); + } + + currentBracket = textEditorModel.findNextBracket(new Position(nextLineNumber, nextColumn)); + } + + return new Range(startLineNumber, 1, maxLineNumber + 1, 1); + } + private addDecoration(range: Range, hoverMessage: MarkdownString): void { const newDecorations: IModelDeltaDecoration = {