diff --git a/README.md b/README.md
index 91566a493db..5d5ee55c63a 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,8 @@
a code editor with what developers need for their core edit-build-debug cycle. Code
provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools.
+VS Code is updated monthly with new features and bug fixes. You can download it for Windows, Mac and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily.
+
diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js
index 5ecb9a1f315..404b81bfff5 100644
--- a/build/gulpfile.vscode.js
+++ b/build/gulpfile.vscode.js
@@ -269,7 +269,7 @@ function packageTask(platform, arch, opts) {
.pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node']))
.pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
.pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/index.js']))
- .pipe(util.cleanNodeModule('pty.js', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/**']));
+ .pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/**']));
let all = es.merge(
packageJsonStream,
diff --git a/extensions/html/OSSREADME.json b/extensions/html/OSSREADME.json
index 9949448fbcb..238e37e068f 100644
--- a/extensions/html/OSSREADME.json
+++ b/extensions/html/OSSREADME.json
@@ -1,7 +1,7 @@
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "js-beautify",
- "version": "1.6.4",
+ "version": "1.6.8",
"license": "MIT",
"repositoryURL": "https://github.com/beautify-web/js-beautify"
},{
diff --git a/extensions/html/package.json b/extensions/html/package.json
index b7d34877f26..db0bb0c3a0c 100644
--- a/extensions/html/package.json
+++ b/extensions/html/package.json
@@ -87,9 +87,17 @@
"string",
"null"
],
- "default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, pre, q, samp, select, small, span, strong, sub, sup, textarea, tt, var",
+ "default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, q, samp, select, small, span, strong, sub, sup, textarea, tt, var",
"description": "%html.format.unformatted.desc%"
},
+ "html.format.contentUnformatted": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": "pre",
+ "description": "%html.format.contentUnformatted.desc%"
+ },
"html.format.indentInnerHtml": {
"type": "boolean",
"default": false,
@@ -126,6 +134,13 @@
"default": "head, body, /html",
"description": "%html.format.extraLiners.desc%"
},
+ "html.format.wrapAttributes": {
+ "type": "string",
+ "default": "auto",
+ "enum": [ "auto", "force", "force-align", "force-expand-multiline" ],
+ "enumDescriptions": ["%html.format.wrapAttributes.auto%", "%html.format.wrapAttributes.force%", "%html.format.wrapAttributes.forcealign%", "%html.format.wrapAttributes.forcemultiline%"],
+ "description": "%html.format.wrapAttributes.desc%"
+ },
"html.suggest.angular1": {
"type": "boolean",
"default": true,
diff --git a/extensions/html/package.nls.json b/extensions/html/package.nls.json
index f66e083890f..b27892fe954 100644
--- a/extensions/html/package.nls.json
+++ b/extensions/html/package.nls.json
@@ -2,12 +2,18 @@
"html.format.enable.desc": "Enable/disable default HTML formatter (requires restart)",
"html.format.wrapLineLength.desc": "Maximum amount of characters per line (0 = disable).",
"html.format.unformatted.desc": "List of tags, comma separated, that shouldn't be reformatted. 'null' defaults to all tags listed at https://www.w3.org/TR/html5/dom.html#phrasing-content.",
+ "html.format.contentUnformatted.desc": "List of tags, comma separated, where the content shouldn't be reformatted. 'null' defaults to the 'pre' tag.",
"html.format.indentInnerHtml.desc": "Indent and sections.",
"html.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.",
"html.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk. Use 'null' for unlimited.",
"html.format.indentHandlebars.desc": "Format and indent {{#foo}} and {{/foo}}.",
"html.format.endWithNewline.desc": "End with a newline.",
"html.format.extraLiners.desc": "List of tags, comma separated, that should have an extra newline before them. 'null' defaults to \"head, body, /html\".",
+ "html.format.wrapAttributes.desc": "Wrap attributes.",
+ "html.format.wrapAttributes.auto": "Wrap attributes only when line length is exceeded.",
+ "html.format.wrapAttributes.force": "Wrap each attribute except first.",
+ "html.format.wrapAttributes.forcealign": "Wrap each attribute except first and keep aligned.",
+ "html.format.wrapAttributes.forcemultiline": "Wrap each attribute.",
"html.suggest.angular1.desc": "Configures if the built-in HTML language support suggests Angular V1 tags and properties.",
"html.suggest.ionic.desc": "Configures if the built-in HTML language support suggests Ionic tags, properties and values.",
"html.suggest.html5.desc":"Configures if the built-in HTML language support suggests HTML5 tags, properties and values.",
diff --git a/extensions/html/server/npm-shrinkwrap.json b/extensions/html/server/npm-shrinkwrap.json
index be8c7759112..598012b6d8b 100644
--- a/extensions/html/server/npm-shrinkwrap.json
+++ b/extensions/html/server/npm-shrinkwrap.json
@@ -2,15 +2,20 @@
"name": "vscode-html-languageserver",
"version": "1.0.0",
"dependencies": {
+ "@types/node": {
+ "version": "6.0.58",
+ "from": "@types/node@>=6.0.51 <7.0.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.58.tgz"
+ },
"vscode-css-languageservice": {
"version": "2.0.0-next.6",
"from": "vscode-css-languageservice@next",
"resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.0.0-next.6.tgz"
},
"vscode-html-languageservice": {
- "version": "2.0.0-next.3",
+ "version": "2.0.0-next.5",
"from": "vscode-html-languageservice@next",
- "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.0-next.3.tgz"
+ "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.0-next.5.tgz"
},
"vscode-jsonrpc": {
"version": "3.0.1-alpha.2",
diff --git a/extensions/html/server/package.json b/extensions/html/server/package.json
index e6d4b8cf772..fc6d4a05e98 100644
--- a/extensions/html/server/package.json
+++ b/extensions/html/server/package.json
@@ -9,7 +9,7 @@
},
"dependencies": {
"vscode-css-languageservice": "^2.0.0-next.6",
- "vscode-html-languageservice": "^2.0.0-next.3",
+ "vscode-html-languageservice": "^2.0.0-next.5",
"vscode-languageserver": "3.0.1-alpha.2",
"vscode-nls": "^1.0.7",
"vscode-uri": "^1.0.0"
diff --git a/extensions/javascript/language-configuration.json b/extensions/javascript/javascript-language-configuration.json
similarity index 100%
rename from extensions/javascript/language-configuration.json
rename to extensions/javascript/javascript-language-configuration.json
diff --git a/extensions/javascript/package.json b/extensions/javascript/package.json
index b08729bbe12..8385c006d33 100644
--- a/extensions/javascript/package.json
+++ b/extensions/javascript/package.json
@@ -27,7 +27,7 @@
"extensions": [
".jsx"
],
- "configuration": "./language-configuration.json"
+ "configuration": "./javascript-language-configuration.json"
},
{
"id": "javascript",
@@ -47,19 +47,31 @@
"mimetypes": [
"text/javascript"
],
- "configuration": "./language-configuration.json"
+ "configuration": "./javascript-language-configuration.json"
+ },
+ {
+ "id": "jsx-tags",
+ "configuration": "./tags-language-configuration.json"
}
],
"grammars": [
{
"language": "javascriptreact",
"scopeName": "source.js",
- "path": "./syntaxes/JavaScript.tmLanguage.json"
+ "path": "./syntaxes/JavaScript.tmLanguage.json",
+ "embeddedLanguages": {
+ "meta.tag.js": "jsx-tags",
+ "meta.tag.without-attributes.js": "jsx-tags"
+ }
},
{
"language": "javascript",
"scopeName": "source.js",
- "path": "./syntaxes/JavaScript.tmLanguage.json"
+ "path": "./syntaxes/JavaScript.tmLanguage.json",
+ "embeddedLanguages": {
+ "meta.tag.js": "jsx-tags",
+ "meta.tag.without-attributes.js": "jsx-tags"
+ }
},
{
"scopeName": "source.js.regexp",
diff --git a/extensions/javascript/tags-language-configuration.json b/extensions/javascript/tags-language-configuration.json
new file mode 100644
index 00000000000..fa04cf1756f
--- /dev/null
+++ b/extensions/javascript/tags-language-configuration.json
@@ -0,0 +1,27 @@
+{
+ "comments": {
+ "blockComment": [ "{/*", "*/}" ]
+ },
+ "brackets": [
+ ["{", "}"],
+ ["[", "]"],
+ ["(", ")"],
+ ["<", ">"]
+ ],
+ "autoClosingPairs": [
+ { "open": "{", "close": "}" },
+ { "open": "[", "close": "]" },
+ { "open": "(", "close": ")" },
+ { "open": "'", "close": "'", "notIn": ["string", "comment"] },
+ { "open": "\"", "close": "\"", "notIn": ["string"] },
+ { "open": "/**", "close": " */", "notIn": ["string"] }
+ ],
+ "surroundingPairs": [
+ ["{", "}"],
+ ["[", "]"],
+ ["(", ")"],
+ ["<", ">"],
+ ["'", "'"],
+ ["\"", "\""]
+ ]
+}
\ No newline at end of file
diff --git a/extensions/json/server/npm-shrinkwrap.json b/extensions/json/server/npm-shrinkwrap.json
index f65223e7872..c5d6a6aa9cf 100644
--- a/extensions/json/server/npm-shrinkwrap.json
+++ b/extensions/json/server/npm-shrinkwrap.json
@@ -43,9 +43,9 @@
"resolved": "https://registry.npmjs.org/request-light/-/request-light-0.1.0.tgz"
},
"vscode-json-languageservice": {
- "version": "2.0.0-next.8",
+ "version": "2.0.0-next.9",
"from": "vscode-json-languageservice@next",
- "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.0-next.8.tgz"
+ "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.0-next.9.tgz"
},
"vscode-jsonrpc": {
"version": "3.0.1-alpha.2",
diff --git a/extensions/json/server/package.json b/extensions/json/server/package.json
index 3b4d77f71e3..2ec6d081bca 100644
--- a/extensions/json/server/package.json
+++ b/extensions/json/server/package.json
@@ -9,7 +9,7 @@
},
"dependencies": {
"request-light": "^0.1.0",
- "vscode-json-languageservice": "^2.0.0-next.8",
+ "vscode-json-languageservice": "^2.0.0-next.9",
"vscode-languageserver": "3.0.1-alpha.3",
"vscode-nls": "^1.0.7"
},
diff --git a/extensions/typescript/package.json b/extensions/typescript/package.json
index 201ddf039de..ae1c645c03e 100644
--- a/extensions/typescript/package.json
+++ b/extensions/typescript/package.json
@@ -68,7 +68,11 @@
{
"language": "typescriptreact",
"scopeName": "source.tsx",
- "path": "./syntaxes/TypeScriptReact.tmLanguage.json"
+ "path": "./syntaxes/TypeScriptReact.tmLanguage.json",
+ "embeddedLanguages": {
+ "meta.tag.tsx": "jsx-tags",
+ "meta.tag.without-attributes.tsx": "jsx-tags"
+ }
}
],
"configuration": {
@@ -99,6 +103,11 @@
"default": true,
"description": "%typescript.check.tscVersion%"
},
+ "typescript.referencesCodeLens.enabled": {
+ "type": "boolean",
+ "default": false,
+ "description": "%typescript.referencesCodeLens.enabled%"
+ },
"typescript.tsserver.trace": {
"type": "string",
"enum": [
diff --git a/extensions/typescript/package.nls.json b/extensions/typescript/package.nls.json
index 51b9649ab91..1834876a6d9 100644
--- a/extensions/typescript/package.nls.json
+++ b/extensions/typescript/package.nls.json
@@ -24,5 +24,6 @@
"format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Defines space handling after opening and before closing JSX expression braces. Requires TypeScript >= 2.0.6.",
"format.placeOpenBraceOnNewLineForFunctions": "Defines whether an open brace is put onto a new line for functions or not.",
"format.placeOpenBraceOnNewLineForControlBlocks": "Defines whether an open brace is put onto a new line for control blocks or not.",
- "javascript.validate.enable": "Enable/disable JavaScript validation."
+ "javascript.validate.enable": "Enable/disable JavaScript validation.",
+ "typescript.referencesCodeLens.enabled": "Enable/disable the references code lens"
}
\ No newline at end of file
diff --git a/extensions/typescript/src/features/referencesCodeLensProvider.ts b/extensions/typescript/src/features/referencesCodeLensProvider.ts
new file mode 100644
index 00000000000..145859cecb2
--- /dev/null
+++ b/extensions/typescript/src/features/referencesCodeLensProvider.ts
@@ -0,0 +1,144 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 { CodeLensProvider, CodeLens, CancellationToken, TextDocument, Range, Uri, Location, Position, workspace, WorkspaceConfiguration } from 'vscode';
+import * as Proto from '../protocol';
+import * as PConst from '../protocol.const';
+
+import { ITypescriptServiceClient } from '../typescriptService';
+
+import * as nls from 'vscode-nls';
+let localize = nls.loadMessageBundle();
+
+
+class ReferencesCodeLens extends CodeLens {
+ public document: Uri;
+ public file: string;
+
+ constructor(document: Uri, file: string, range: Range) {
+ super(range);
+ this.document = document;
+ this.file = file;
+ }
+}
+
+export default class TypeScriptReferencesCodeLensProvider implements CodeLensProvider {
+ private client: ITypescriptServiceClient;
+ private enabled = false;
+
+ constructor(client: ITypescriptServiceClient) {
+ this.client = client;
+ }
+
+ public updateConfiguration(config: WorkspaceConfiguration): void {
+ let typeScriptConfig = workspace.getConfiguration('typescript');
+ this.enabled = typeScriptConfig.get('referencesCodeLens.enabled', false);
+ }
+
+ provideCodeLenses(document: TextDocument, token: CancellationToken): Promise {
+ if (!this.enabled) {
+ return Promise.resolve([]);
+ }
+
+ const filepath = this.client.asAbsolutePath(document.uri);
+ if (!filepath) {
+ return Promise.resolve([]);
+ }
+ return this.client.execute('navtree', { file: filepath }, token).then(response => {
+ const tree = response.body;
+ const referenceableSpans: Range[] = [];
+ if (tree && tree.childItems) {
+ tree.childItems.forEach(item => this.extractReferenceableSymbols(document, item, referenceableSpans));
+ }
+ return Promise.resolve(referenceableSpans.map(span => new ReferencesCodeLens(document.uri, filepath, span)));
+ });
+ }
+
+ resolveCodeLens(inputCodeLens: CodeLens, token: CancellationToken): Promise {
+ const codeLens = inputCodeLens as ReferencesCodeLens;
+ if (!codeLens.document) {
+ return Promise.reject(codeLens);
+ }
+ const args: Proto.FileLocationRequestArgs = {
+ file: codeLens.file,
+ line: codeLens.range.start.line + 1,
+ offset: codeLens.range.start.character + 1
+ };
+ return this.client.execute('references', args, token).then(response => {
+ if (response && response.body) {
+ const referenceCount = Math.max(0, response.body.refs.length - 1);
+ const locations = response.body.refs.map(reference =>
+ new Location(Uri.file(reference.file),
+ new Range(
+ new Position(reference.start.line - 1, reference.start.offset - 1),
+ new Position(reference.end.line - 1, reference.end.offset - 1))));
+
+ codeLens.command = {
+ title: referenceCount + ' ' + (referenceCount === 1 ? localize('oneReferenceLabel', 'reference') : localize('manyReferenceLabel', 'references')),
+ command: 'editor.action.showReferences',
+ arguments: [codeLens.document, codeLens.range.start, locations]
+ };
+ return Promise.resolve(codeLens);
+ }
+ return Promise.reject(codeLens);
+ }).catch(() => {
+ codeLens.command = {
+ title: localize('referenceErrorLabel', 'Could not determine references'),
+ command: ''
+ };
+ return Promise.resolve(codeLens);
+ });
+ }
+
+ private extractReferenceableSymbols(document: TextDocument, item: Proto.NavigationTree, results: Range[]) {
+ if (!item) {
+ return;
+ }
+
+ const span = item.spans && item.spans[0];
+ if (span) {
+ const range = new Range(
+ new Position(span.start.line - 1, span.start.offset - 1),
+ new Position(span.end.line - 1, span.end.offset - 1));
+
+ // TODO: TS currently requires the position for 'references 'to be inside of the identifer
+ // Massage the range to make sure this is the case
+ const text = document.getText(range);
+
+ switch (item.kind) {
+ case PConst.Kind.const:
+ case PConst.Kind.let:
+ case PConst.Kind.variable:
+ case PConst.Kind.function:
+ // Only show references for exported variables
+ if (!item.kindModifiers.match(/\bexport\b/)) {
+ break;
+ }
+ // fallthrough
+
+ case PConst.Kind.memberFunction:
+ case PConst.Kind.memberVariable:
+ case PConst.Kind.memberGetAccessor:
+ case PConst.Kind.memberSetAccessor:
+ case PConst.Kind.constructorImplementation:
+ case PConst.Kind.class:
+ case PConst.Kind.interface:
+ case PConst.Kind.type:
+ case PConst.Kind.enum:
+ const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${item.text}`, 'g');
+ const match = identifierMatch.exec(text);
+ const start = match ? match.index + match[1].length : 0;
+ results.push(new Range(
+ new Position(range.start.line, range.start.character + start),
+ new Position(range.start.line, range.start.character + start + item.text.length)));
+ break;
+ }
+ }
+
+ (item.childItems || []).forEach(item => this.extractReferenceableSymbols(document, item, results));
+ }
+};
\ No newline at end of file
diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts
index 16080631a3d..02891af3f94 100644
--- a/extensions/typescript/src/typescriptMain.ts
+++ b/extensions/typescript/src/typescriptMain.ts
@@ -35,6 +35,7 @@ import BufferSyncSupport from './features/bufferSyncSupport';
import CompletionItemProvider from './features/completionItemProvider';
import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider';
+import ReferenceCodeLensProvider from './features/referencesCodeLensProvider';
import * as BuildStatus from './utils/buildStatus';
import * as ProjectStatus from './utils/projectStatus';
@@ -107,6 +108,7 @@ class LanguageProvider {
private formattingProvider: FormattingProvider;
private formattingProviderRegistration: Disposable | null;
private typingsStatus: TypingsStatus;
+ private referenceCodeLensProvider: ReferenceCodeLensProvider;
private _validate: boolean;
@@ -156,6 +158,12 @@ class LanguageProvider {
this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
}
+ this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client);
+ this.referenceCodeLensProvider.updateConfiguration(config);
+ if (client.apiVersion.has206Features()) {
+ languages.registerCodeLensProvider(this.description.modeIds, this.referenceCodeLensProvider);
+ }
+
this.description.modeIds.forEach(modeId => {
let selector: DocumentFilter = { scheme: 'file', language: modeId };
languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.');
@@ -171,6 +179,7 @@ class LanguageProvider {
if (client.apiVersion.has213Features()) {
languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId));
}
+
languages.setLanguageConfiguration(modeId, {
indentationRules: {
// ^(.*\*/)?\s*\}.*$
@@ -217,6 +226,9 @@ class LanguageProvider {
if (this.completionItemProvider) {
this.completionItemProvider.updateConfiguration(config);
}
+ if (this.referenceCodeLensProvider) {
+ this.referenceCodeLensProvider.updateConfiguration(config);
+ }
if (this.formattingProvider) {
this.formattingProvider.updateConfiguration(config);
if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index cc2ddaa5560..66e8c0861dd 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -332,10 +332,10 @@
"from": "process-nextick-args@>=1.0.6 <1.1.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
},
- "pty.js": {
- "version": "0.3.0",
- "from": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642",
- "resolved": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642",
+ "node-pty": {
+ "version": "0.4.1",
+ "from": "node-pty@0.4.1",
+ "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.4.1.tgz",
"dependencies": {
"extend": {
"version": "1.2.1",
@@ -430,9 +430,9 @@
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.0.tgz"
},
"xterm": {
- "version": "2.2.0",
+ "version": "2.2.3",
"from": "git+https://github.com/Tyriar/xterm.js.git#vscode-release/1.9",
- "resolved": "git+https://github.com/Tyriar/xterm.js.git#618e6bbd0a0ebaabc4b06ab10bea89768ded62b5"
+ "resolved": "git+https://github.com/Tyriar/xterm.js.git#01dd436a56ee2370fa9b5aa5bc2e138b22799eda"
},
"yauzl": {
"version": "2.3.1",
diff --git a/package.json b/package.json
index 529cb07f99c..f3f4fb2a155 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
"iconv-lite": "0.4.15",
"minimist": "1.2.0",
"native-keymap": "0.3.0",
- "pty.js": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642",
+ "node-pty": "0.4.1",
"semver": "4.3.6",
"vscode-debugprotocol": "1.15.0",
"vscode-textmate": "3.1.0",
diff --git a/src/typings/pty.js.d.ts b/src/typings/node-pty.d.ts
similarity index 96%
rename from src/typings/pty.js.d.ts
rename to src/typings/node-pty.d.ts
index cc726238aa2..ef417f1156e 100644
--- a/src/typings/pty.js.d.ts
+++ b/src/typings/node-pty.d.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-declare module 'pty.js' {
+declare module 'node-pty' {
export function fork(file: string, args: string[], options: any): Terminal;
export function spawn(file: string, args: string[], options: any): Terminal;
export function createTerminal(file: string, args: string[], options: any): Terminal;
diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts
index a22159dfc85..f8f7a40075b 100644
--- a/src/vs/base/common/json.ts
+++ b/src/vs/base/common/json.ts
@@ -1043,9 +1043,6 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseString(isValue: boolean): boolean {
- if (_scanner.getToken() !== SyntaxKind.StringLiteral) {
- return false;
- }
let value = _scanner.getTokenValue();
if (isValue) {
onLiteralValue(value);
@@ -1088,10 +1085,11 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseProperty(): boolean {
- if (!parseString(false)) {
+ if (_scanner.getToken() !== SyntaxKind.StringLiteral) {
handleError(ParseErrorCode.PropertyNameExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]);
return false;
}
+ parseString(false);
if (_scanner.getToken() === SyntaxKind.ColonToken) {
onSeparator(':');
scanNext(); // consume colon
@@ -1106,9 +1104,6 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseObject(): boolean {
- if (_scanner.getToken() !== SyntaxKind.OpenBraceToken) {
- return false;
- }
onObjectBegin();
scanNext(); // consume open brace
@@ -1138,9 +1133,6 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseArray(): boolean {
- if (_scanner.getToken() !== SyntaxKind.OpenBracketToken) {
- return false;
- }
onArrayBegin();
scanNext(); // consume open bracket
@@ -1170,7 +1162,16 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseValue(): boolean {
- return parseArray() || parseObject() || parseString(true) || parseLiteral();
+ switch (_scanner.getToken()) {
+ case SyntaxKind.OpenBracketToken:
+ return parseArray();
+ case SyntaxKind.OpenBraceToken:
+ return parseObject();
+ case SyntaxKind.StringLiteral:
+ return parseString(true);
+ default:
+ return parseLiteral();
+ }
}
scanNext();
diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts
index 5eb0da6e884..ef6a310c847 100644
--- a/src/vs/code/electron-main/main.ts
+++ b/src/vs/code/electron-main/main.ts
@@ -261,8 +261,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
}
// Install Menu
- const menu = instantiationService2.createInstance(VSCodeMenu);
- menu.ready();
+ instantiationService2.createInstance(VSCodeMenu);
});
}
diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts
index 7bfc3bc7ec3..df488646937 100644
--- a/src/vs/code/electron-main/menus.ts
+++ b/src/vs/code/electron-main/menus.ts
@@ -21,6 +21,10 @@ import { Keybinding } from 'vs/base/common/keyCodes';
import { KeybindingLabels } from 'vs/base/common/keybinding';
import product from 'vs/platform/node/product';
import { RunOnceScheduler } from 'vs/base/common/async';
+import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import Event, { Emitter, once } from 'vs/base/common/event';
+import { ConfigWatcher } from 'vs/base/node/config';
+import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
interface IResolvedKeybinding {
id: string;
@@ -46,10 +50,97 @@ interface IConfiguration extends IFilesConfiguration {
};
}
-export class VSCodeMenu {
+class KeybindingsResolver {
private static lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings';
+ private commandIds: Set;
+ private keybindings: { [commandId: string]: string };
+ private keybindingsWatcher: ConfigWatcher;
+
+ private _onKeybindingsChanged = new Emitter();
+ onKeybindingsChanged: Event = this._onKeybindingsChanged.event;
+
+ constructor(
+ @IStorageService private storageService: IStorageService,
+ @IEnvironmentService environmentService: IEnvironmentService,
+ @IWindowsMainService private windowsService: IWindowsMainService
+ ) {
+ this.commandIds = new Set();
+ this.keybindings = this.storageService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null);
+ this.keybindingsWatcher = new ConfigWatcher(environmentService.appKeybindingsPath, { changeBufferDelay: 1000 /* update after 1s */ });
+
+ this.registerListeners();
+ }
+
+ private registerListeners(): void {
+
+ // Resolve keybindings when any first window is loaded
+ const onceOnWindowReady = once(this.windowsService.onWindowReady);
+ onceOnWindowReady(win => this.resolveKeybindings(win));
+
+ // Listen to resolved keybindings from window
+ ipc.on('vscode:keybindingsResolved', (event, rawKeybindings: string) => {
+ let keybindings: IResolvedKeybinding[] = [];
+ try {
+ keybindings = JSON.parse(rawKeybindings);
+ } catch (error) {
+ // Should not happen
+ }
+
+ // Fill hash map of resolved keybindings and check for changes
+ let keybindingsChanged = false;
+ let keybindingsCount = 0;
+ keybindings.forEach(keybinding => {
+ const accelerator = KeybindingLabels._toElectronAccelerator(new Keybinding(keybinding.binding));
+ if (accelerator) {
+ keybindingsCount++;
+
+ if (accelerator !== this.keybindings[keybinding.id]) {
+ this.keybindings[keybinding.id] = accelerator;
+ keybindingsChanged = true;
+ }
+ }
+ });
+
+ // A keybinding might have been unassigned, so we have to account for that too
+ if (Object.keys(this.keybindings).length !== keybindingsCount) {
+ keybindingsChanged = true;
+ }
+
+ if (keybindingsChanged) {
+ this.storageService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart
+
+ this._onKeybindingsChanged.fire();
+ }
+ });
+
+ // Resolve keybindings again when keybindings.json changes
+ this.keybindingsWatcher.onDidUpdateConfiguration(() => this.resolveKeybindings());
+
+ // Resolve keybindings when window reloads because an installed extension could have an impact
+ this.windowsService.onWindowReload(() => this.resolveKeybindings());
+ }
+
+ private resolveKeybindings(win: VSCodeWindow = this.windowsService.getLastActiveWindow()): void {
+ if (this.commandIds.size && win) {
+ const commandIds = [];
+ this.commandIds.forEach(id => commandIds.push(id));
+ win.sendWhenReady('vscode:resolveKeybindings', JSON.stringify(commandIds));
+ }
+ }
+
+ public getKeybinding(commandId: string): string {
+ if (!this.commandIds.has(commandId)) {
+ this.commandIds.add(commandId);
+ }
+
+ return this.keybindings[commandId];
+ }
+}
+
+export class VSCodeMenu {
+
private static MAX_MENU_RECENT_ENTRIES = 10;
private currentAutoSaveSetting: string;
@@ -62,35 +153,28 @@ export class VSCodeMenu {
private menuUpdater: RunOnceScheduler;
- private actionIdKeybindingRequests: string[];
- private mapLastKnownKeybindingToActionId: { [id: string]: string; };
- private mapResolvedKeybindingToActionId: { [id: string]: string; };
- private keybindingsResolved: boolean;
+ private keybindingsResolver: KeybindingsResolver;
private extensionViewlets: IExtensionViewlet[];
constructor(
- @IStorageService private storageService: IStorageService,
@IUpdateService private updateService: IUpdateService,
+ @IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService private configurationService: IConfigurationService,
@IWindowsMainService private windowsService: IWindowsMainService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService private telemetryService: ITelemetryService
) {
- this.actionIdKeybindingRequests = [];
this.extensionViewlets = [];
- this.mapResolvedKeybindingToActionId = Object.create(null);
- this.mapLastKnownKeybindingToActionId = this.storageService.getItem<{ [id: string]: string; }>(VSCodeMenu.lastKnownKeybindingsMapStorageKey) || Object.create(null);
-
this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
+ this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver);
this.onConfigurationUpdated(this.configurationService.getConfiguration());
- }
- public ready(): void {
- this.registerListeners();
this.install();
+
+ this.registerListeners();
}
private registerListeners(): void {
@@ -105,43 +189,6 @@ export class VSCodeMenu {
this.windowsService.onRecentPathsChange(paths => this.updateMenu());
this.windowsService.onWindowClose(_ => this.onClose(this.windowsService.getWindowCount()));
- // Resolve keybindings when any first workbench is loaded
- this.windowsService.onWindowReady(win => this.resolveKeybindings(win));
-
- // Listen to resolved keybindings
- ipc.on('vscode:keybindingsResolved', (event, rawKeybindings) => {
- let keybindings: IResolvedKeybinding[] = [];
- try {
- keybindings = JSON.parse(rawKeybindings);
- } catch (error) {
- // Should not happen
- }
-
- // Fill hash map of resolved keybindings
- let needsMenuUpdate = false;
- keybindings.forEach(keybinding => {
- const accelerator = KeybindingLabels._toElectronAccelerator(new Keybinding(keybinding.binding));
- if (accelerator) {
- this.mapResolvedKeybindingToActionId[keybinding.id] = accelerator;
- if (this.mapLastKnownKeybindingToActionId[keybinding.id] !== accelerator) {
- needsMenuUpdate = true; // we only need to update when something changed!
- }
- }
- });
-
- // A keybinding might have been unassigned, so we have to account for that too
- if (Object.keys(this.mapLastKnownKeybindingToActionId).length !== Object.keys(this.mapResolvedKeybindingToActionId).length) {
- needsMenuUpdate = true;
- }
-
- if (needsMenuUpdate) {
- this.storageService.setItem(VSCodeMenu.lastKnownKeybindingsMapStorageKey, this.mapResolvedKeybindingToActionId); // keep to restore instantly after restart
- this.mapLastKnownKeybindingToActionId = this.mapResolvedKeybindingToActionId; // update our last known map
-
- this.updateMenu();
- }
- });
-
// Listen to extension viewlets
ipc.on('vscode:extensionViewlets', (event, rawExtensionViewlets) => {
let extensionViewlets: IExtensionViewlet[] = [];
@@ -162,6 +209,9 @@ export class VSCodeMenu {
// Listen to update service
this.updateService.onStateChange(() => this.updateMenu());
+
+ // Listen to keybindings change
+ this.keybindingsResolver.onKeybindingsChanged(() => this.updateMenu());
}
private onConfigurationUpdated(config: IConfiguration, handleMenu?: boolean): void {
@@ -201,19 +251,6 @@ export class VSCodeMenu {
}
}
- private resolveKeybindings(win: VSCodeWindow): void {
- if (this.keybindingsResolved) {
- return; // only resolve once
- }
-
- this.keybindingsResolved = true;
-
- // Resolve keybindings when workbench window is up
- if (this.actionIdKeybindingRequests.length) {
- win.send('vscode:resolveKeybindings', JSON.stringify(this.actionIdKeybindingRequests));
- }
- }
-
private updateMenu(): void {
this.menuUpdater.schedule(); // buffer multiple attempts to update the menu
}
@@ -970,19 +1007,7 @@ export class VSCodeMenu {
private getAccelerator(actionId: string, fallback?: string): string {
if (actionId) {
- const resolvedKeybinding = this.mapResolvedKeybindingToActionId[actionId];
- if (resolvedKeybinding) {
- return resolvedKeybinding; // keybinding is fully resolved
- }
-
- if (!this.keybindingsResolved) {
- this.actionIdKeybindingRequests.push(actionId); // keybinding needs to be resolved
- }
-
- const lastKnownKeybinding = this.mapLastKnownKeybindingToActionId[actionId];
- if (lastKnownKeybinding) {
- return lastKnownKeybinding; // return the last known keybining (chance of mismatch is very low unless it changed)
- }
+ return this.keybindingsResolver.getKeybinding(actionId);
}
return fallback;
diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts
index dd967a6e14c..67e2d66ac62 100644
--- a/src/vs/code/electron-main/window.ts
+++ b/src/vs/code/electron-main/window.ts
@@ -8,6 +8,7 @@
import * as path from 'path';
import * as platform from 'vs/base/common/platform';
import * as objects from 'vs/base/common/objects';
+import nls = require('vs/nls');
import { IStorageService } from 'vs/code/electron-main/storage';
import { shell, screen, BrowserWindow, systemPreferences, app } from 'electron';
import { TPromise, TValueCallback } from 'vs/base/common/winjs.base';
@@ -117,6 +118,12 @@ export enum ReadyState {
READY
}
+interface IConfiguration {
+ window: {
+ menuBarVisibility: 'visible' | 'toggle' | 'hidden';
+ };
+}
+
export interface IVSCodeWindow {
id: number;
readyState: ReadyState;
@@ -127,7 +134,6 @@ export interface IVSCodeWindow {
export class VSCodeWindow implements IVSCodeWindow {
- public static menuBarHiddenKey = 'menuBarHidden';
public static colorThemeStorageKey = 'theme';
private static MIN_WIDTH = 200;
@@ -143,6 +149,7 @@ export class VSCodeWindow implements IVSCodeWindow {
private _extensionDevelopmentPath: string;
private _isExtensionTestHost: boolean;
private windowState: IWindowState;
+ private currentMenuBarVisibility: 'visible' | 'toggle' | 'hidden';
private currentWindowMode: WindowMode;
private whenReadyCallbacks: TValueCallback[];
@@ -220,15 +227,14 @@ export class VSCodeWindow implements IVSCodeWindow {
this._lastFocusTime = Date.now(); // since we show directly, we need to set the last focus time too
- if (this.storageService.getItem(VSCodeWindow.menuBarHiddenKey, false)) {
- this.setMenuBarVisibility(false); // respect configured menu bar visibility
- }
+ // respect configured menu bar visibility
+ this.onConfigurationUpdated(this.configurationService.getConfiguration());
- // TODO@joao: hook this up to some initialization routine
- // this causes a race between setting the headers and doing
+ // TODO@joao: hook this up to some initialization routine this causes a race between setting the headers and doing
// a request that needs them. chances are low
this.setCommonHTTPHeaders();
+ // Eventing
this.registerListeners();
}
@@ -407,8 +413,19 @@ export class VSCodeWindow implements IVSCodeWindow {
}
});
}
+
+ // Handle configuration changes
+ this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(e.config));
}
+ private onConfigurationUpdated(config: IConfiguration): void {
+ const newMenuBarVisibility = this.getMenuBarVisibility(config);
+ if (newMenuBarVisibility !== this.currentMenuBarVisibility) {
+ this.currentMenuBarVisibility = newMenuBarVisibility;
+ this.setMenuBarVisibility(newMenuBarVisibility);
+ }
+ };
+
public load(config: IWindowConfiguration): void {
// If this is the first time the window is loaded, we associate the paths
@@ -656,21 +673,53 @@ export class VSCodeWindow implements IVSCodeWindow {
public toggleFullScreen(): void {
const willBeFullScreen = !this.win.isFullScreen();
+ // set fullscreen flag on window
this.win.setFullScreen(willBeFullScreen);
- // Windows & Linux: Hide the menu bar but still allow to bring it up by pressing the Alt key
- if (platform.isWindows || platform.isLinux) {
- if (willBeFullScreen) {
- this.setMenuBarVisibility(false);
- } else {
- this.setMenuBarVisibility(!this.storageService.getItem(VSCodeWindow.menuBarHiddenKey, false)); // restore as configured
- }
- }
+ // respect configured menu bar visibility or default to toggle if not set
+ this.setMenuBarVisibility(this.getMenuBarVisibility(this.configurationService.getConfiguration(), willBeFullScreen ? 'toggle' : 'visible'), false);
}
- public setMenuBarVisibility(visible: boolean): void {
- this.win.setMenuBarVisibility(visible);
- this.win.setAutoHideMenuBar(!visible);
+ private getMenuBarVisibility(configuration: IConfiguration, fallback: 'visible' | 'toggle' | 'hidden' = 'visible'): 'visible' | 'toggle' | 'hidden' {
+ const windowConfig = this.configurationService.getConfiguration('window');
+
+ if (!windowConfig || !windowConfig.menuBarVisibility) {
+ return fallback;
+ }
+
+ let menuBarVisibility = windowConfig.menuBarVisibility;
+ if (['visible', 'toggle', 'hidden'].indexOf(menuBarVisibility) < 0) {
+ menuBarVisibility = fallback;
+ }
+
+ return menuBarVisibility;
+ }
+
+ public setMenuBarVisibility(visibility: 'visible' | 'toggle' | 'hidden', notify: boolean = true): void {
+ if (platform.isMacintosh) {
+ return; // ignore for macOS platform
+ }
+
+ switch (visibility) {
+ case ('visible'):
+ this.win.setMenuBarVisibility(true);
+ this.win.setAutoHideMenuBar(false);
+ break;
+
+ case ('toggle'):
+ this.win.setMenuBarVisibility(false);
+ this.win.setAutoHideMenuBar(true);
+
+ if (notify) {
+ this.send('vscode:showInfoMessage', nls.localize('hiddenMenuBar', "You can still access the menu bar by pressing the **Alt** key."));
+ };
+ break;
+
+ case ('hidden'):
+ this.win.setMenuBarVisibility(false);
+ this.win.setAutoHideMenuBar(false);
+ break;
+ };
}
public sendWhenReady(channel: string, ...args: any[]): void {
diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts
index 7c442c543d9..f9084d15803 100644
--- a/src/vs/code/electron-main/windows.ts
+++ b/src/vs/code/electron-main/windows.ts
@@ -104,6 +104,7 @@ export interface IWindowsMainService {
// events
onWindowReady: CommonEvent;
onWindowClose: CommonEvent;
+ onWindowReload: CommonEvent;
onPathsOpen: CommonEvent;
onRecentPathsChange: CommonEvent;
@@ -131,7 +132,6 @@ export interface IWindowsMainService {
removeFromRecentPathsList(path: string): void;
removeFromRecentPathsList(paths: string[]): void;
clearRecentPathsList(): void;
- toggleMenuBar(windowId: number): void;
quit(): void;
}
@@ -159,6 +159,9 @@ export class WindowsManager implements IWindowsMainService {
private _onWindowClose = new Emitter();
onWindowClose: CommonEvent = this._onWindowClose.event;
+ private _onWindowReload = new Emitter();
+ onWindowReload: CommonEvent = this._onWindowReload.event;
+
private _onPathsOpen = new Emitter();
onPathsOpen: CommonEvent = this._onPathsOpen.event;
@@ -292,6 +295,9 @@ export class WindowsManager implements IWindowsMainService {
this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => {
if (!veto) {
win.reload(cli);
+
+ // Emit
+ this._onWindowReload.fire(win.id);
}
});
}
@@ -1176,24 +1182,6 @@ export class WindowsManager implements IWindowsMainService {
return pathA === pathB;
}
- public toggleMenuBar(windowId: number): void {
- // Update in settings
- const menuBarHidden = this.storageService.getItem(VSCodeWindow.menuBarHiddenKey, false);
- const newMenuBarHidden = !menuBarHidden;
- this.storageService.setItem(VSCodeWindow.menuBarHiddenKey, newMenuBarHidden);
-
- // Update across windows
- WindowsManager.WINDOWS.forEach(w => w.setMenuBarVisibility(!newMenuBarHidden));
-
- // Inform user if menu bar is now hidden
- if (newMenuBarHidden) {
- const vscodeWindow = this.getWindowById(windowId);
- if (vscodeWindow) {
- vscodeWindow.send('vscode:showInfoMessage', nls.localize('hiddenMenuBar', "You can still access the menu bar by pressing the **Alt** key."));
- }
- }
- }
-
private updateWindowsJumpList(): void {
if (!platform.isWindows) {
return; // only on windows
diff --git a/src/vs/editor/common/model/wordHelper.ts b/src/vs/editor/common/model/wordHelper.ts
index a2e4e65ddbd..8ced8f8c91f 100644
--- a/src/vs/editor/common/model/wordHelper.ts
+++ b/src/vs/editor/common/model/wordHelper.ts
@@ -54,38 +54,69 @@ export function ensureValidWordDefinition(wordDefinition?: RegExp): RegExp {
return result;
}
-export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
+function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
+ // find whitespace enclosed text around column and match from there
- // console.log('_getWordAtText: ', column, text, textOffset);
+ if (wordDefinition.test(' ')) {
+ return getWordAtPosSlow(column, wordDefinition, text, textOffset);
+ }
- var words = text.match(wordDefinition),
- k: number,
- startWord: number,
- endWord: number,
- startColumn: number,
- endColumn: number,
- word: string;
+ let pos = column - 1 - textOffset;
+ let start = text.lastIndexOf(' ', pos - 1) + 1;
+ let end = text.indexOf(' ', pos);
+ if (end === -1) {
+ end = text.length;
+ }
- if (words) {
- for (k = 0; k < words.length; k++) {
- word = words[k].trim();
- if (word.length > 0) {
- startWord = text.indexOf(word, endWord);
- endWord = startWord + word.length;
-
- startColumn = textOffset + startWord + 1;
- endColumn = textOffset + endWord + 1;
-
- if (startColumn <= column && column <= endColumn) {
- return {
- word: word,
- startColumn: startColumn,
- endColumn: endColumn
- };
- }
- }
+ wordDefinition.lastIndex = start;
+ let match: RegExpMatchArray;
+ while (match = wordDefinition.exec(text)) {
+ if (match.index <= pos && wordDefinition.lastIndex >= pos) {
+ return {
+ word: match[0],
+ startColumn: textOffset + 1 + match.index,
+ endColumn: textOffset + 1 + wordDefinition.lastIndex
+ };
}
}
return null;
}
+
+
+function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
+ // matches all words starting at the beginning
+ // of the input until it finds a match that encloses
+ // the desired column. slow but correct
+
+ let pos = column - 1 - textOffset;
+ wordDefinition.lastIndex = 0;
+
+ let match: RegExpMatchArray;
+ while (match = wordDefinition.exec(text)) {
+
+ if (match.index > pos) {
+ // |nW -> matched only after the pos
+ return null;
+
+ } else if (wordDefinition.lastIndex >= pos) {
+ // W|W -> match encloses pos
+ return {
+ word: match[0],
+ startColumn: textOffset + 1 + match.index,
+ endColumn: textOffset + 1 + wordDefinition.lastIndex
+ };
+ }
+ }
+
+ return null;
+}
+
+export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
+ const result = getWordAtPosFast(column, wordDefinition, text, textOffset);
+ // both (getWordAtPosFast and getWordAtPosSlow) leave the wordDefinition-RegExp
+ // in an undefined state and to not confuse other users of the wordDefinition
+ // we reset the lastIndex
+ wordDefinition.lastIndex = 0;
+ return result;
+}
diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts
index b6824caa556..a9388a93a3b 100644
--- a/src/vs/editor/common/services/editorSimpleWorker.ts
+++ b/src/vs/editor/common/services/editorSimpleWorker.ts
@@ -148,8 +148,11 @@ class MirrorModel extends MirrorModel2 implements ICommonModel {
// TODO@Joh, TODO@Alex - remove these and make sure the super-things work
private _wordenize(content: string, wordDefinition: RegExp): editorCommon.IWordRange[] {
- var result: editorCommon.IWordRange[] = [];
- var match: RegExpExecArray;
+ const result: editorCommon.IWordRange[] = [];
+ let match: RegExpExecArray;
+
+ wordDefinition.lastIndex = 0; // reset lastIndex just to be sure
+
while (match = wordDefinition.exec(content)) {
if (match[0].length === 0) {
// it did match the empty string
diff --git a/src/vs/editor/contrib/suggest/common/snippetCompletion.ts b/src/vs/editor/contrib/suggest/common/snippetCompletion.ts
index 61ef6a693fb..8fb21f6bb21 100644
--- a/src/vs/editor/contrib/suggest/common/snippetCompletion.ts
+++ b/src/vs/editor/contrib/suggest/common/snippetCompletion.ts
@@ -22,7 +22,7 @@ interface ISnippetPick extends IPickOpenEntry {
class Args {
static fromUser(arg: any): Args {
- if (typeof arg !== 'object') {
+ if (!arg || typeof arg !== 'object') {
return Args._empty;
}
let {snippet, name, langId} = arg;
diff --git a/src/vs/platform/markers/common/problemMatcher.ts b/src/vs/platform/markers/common/problemMatcher.ts
index 5be9dde216a..05c47eb7164 100644
--- a/src/vs/platform/markers/common/problemMatcher.ts
+++ b/src/vs/platform/markers/common/problemMatcher.ts
@@ -62,12 +62,10 @@ export interface ProblemPattern {
loop?: boolean;
- mostSignifikant?: boolean;
-
[key: string]: any;
}
-export let problemPatternProperties = ['file', 'message', 'location', 'line', 'column', 'endLine', 'endColumn', 'code', 'severity', 'loop', 'mostSignifikant'];
+export let problemPatternProperties = ['file', 'message', 'location', 'line', 'column', 'endLine', 'endColumn', 'code', 'severity', 'loop'];
export interface WatchingPattern {
regexp: RegExp;
diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts
index 51aee630f63..1436678648e 100644
--- a/src/vs/platform/windows/common/windows.ts
+++ b/src/vs/platform/windows/common/windows.ts
@@ -37,7 +37,6 @@ export interface IWindowsService {
maximizeWindow(windowId: number): TPromise;
unmaximizeWindow(windowId: number): TPromise;
setDocumentEdited(windowId: number, flag: boolean): TPromise;
- toggleMenuBar(windowId: number): TPromise;
quit(): TPromise;
// Global methods
@@ -80,7 +79,6 @@ export interface IWindowService {
getRecentlyOpen(): TPromise<{ files: string[]; folders: string[]; }>;
focusWindow(): TPromise;
setDocumentEdited(flag: boolean): TPromise;
- toggleMenuBar(): TPromise;
isMaximized(): TPromise;
maximizeWindow(): TPromise;
unmaximizeWindow(): TPromise;
@@ -94,4 +92,5 @@ export interface IWindowSettings {
zoomLevel: number;
titleBarStyle: 'native' | 'custom';
autoDetectHighContrast: boolean;
+ menuBarVisibility: 'visible' | 'toggle' | 'hidden';
}
diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts
index a02689a28ef..44f477c0ce1 100644
--- a/src/vs/platform/windows/common/windowsIpc.ts
+++ b/src/vs/platform/windows/common/windowsIpc.ts
@@ -29,7 +29,6 @@ export interface IWindowsChannel extends IChannel {
call(command: 'maximizeWindow', arg: number): TPromise;
call(command: 'unmaximizeWindow', arg: number): TPromise;
call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise;
- call(command: 'toggleMenuBar', arg: number): TPromise;
call(command: 'quit'): TPromise;
call(command: 'openWindow', arg: [string[], { forceNewWindow?: boolean, forceReuseWindow?: boolean }]): TPromise;
call(command: 'openNewWindow'): TPromise;
@@ -75,7 +74,6 @@ export class WindowsChannel implements IWindowsChannel {
case 'maximizeWindow': return this.service.maximizeWindow(arg);
case 'unmaximizeWindow': return this.service.unmaximizeWindow(arg);
case 'setDocumentEdited': return this.service.setDocumentEdited(arg[0], arg[1]);
- case 'toggleMenuBar': return this.service.toggleMenuBar(arg);
case 'openWindow': return this.service.openWindow(arg[0], arg[1]);
case 'openNewWindow': return this.service.openNewWindow();
case 'showWindow': return this.service.showWindow(arg);
@@ -171,10 +169,6 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('setDocumentEdited', [windowId, flag]);
}
- toggleMenuBar(windowId: number): TPromise {
- return this.channel.call('toggleMenuBar', windowId);
- }
-
quit(): TPromise {
return this.channel.call('quit');
}
@@ -218,4 +212,4 @@ export class WindowsChannelClient implements IWindowsService {
startCrashReporter(config: Electron.CrashReporterStartOptions): TPromise {
return this.channel.call('startCrashReporter', config);
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts
index fb55d01df3f..4a6704becb8 100644
--- a/src/vs/platform/windows/electron-browser/windowService.ts
+++ b/src/vs/platform/windows/electron-browser/windowService.ts
@@ -89,7 +89,4 @@ export class WindowService implements IWindowService {
return this.windowsService.setDocumentEdited(this.windowId, flag);
}
- toggleMenuBar(): TPromise {
- return this.windowsService.toggleMenuBar(this.windowId);
- }
-}
\ No newline at end of file
+}
diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts
index 7b0e6d47e4b..3e870dab516 100644
--- a/src/vs/platform/windows/electron-main/windowsService.ts
+++ b/src/vs/platform/windows/electron-main/windowsService.ts
@@ -193,11 +193,6 @@ export class WindowsService implements IWindowsService, IDisposable {
return TPromise.as(null);
}
- toggleMenuBar(windowId: number): TPromise {
- this.windowsMainService.toggleMenuBar(windowId);
- return TPromise.as(null);
- }
-
openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise {
if (!paths || !paths.length) {
return TPromise.as(null);
@@ -278,4 +273,4 @@ export class WindowsService implements IWindowsService, IDisposable {
dispose(): void {
this.disposables = dispose(this.disposables);
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts
index 3c1e03ac623..56ef9e955ce 100644
--- a/src/vs/workbench/electron-browser/actions.ts
+++ b/src/vs/workbench/electron-browser/actions.ts
@@ -19,6 +19,7 @@ import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManagementService, LocalExtensionType, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
@@ -169,12 +170,36 @@ export class ToggleMenuBarAction extends Action {
static ID = 'workbench.action.toggleMenuBar';
static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar");
- constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
+ private static menuBarVisibilityKey = 'window.menuBarVisibility';
+
+ constructor(
+ id: string,
+ label: string,
+ @IMessageService private messageService: IMessageService,
+ @IConfigurationService private configurationService: IConfigurationService,
+ @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService
+ ) {
super(id, label);
}
- run(): TPromise {
- return this.windowService.toggleMenuBar();
+ public run(): TPromise {
+ let currentVisibilityValue = this.configurationService.lookup<'visible' | 'toggle' | 'hidden'>(ToggleMenuBarAction.menuBarVisibilityKey).value;
+ if (typeof (currentVisibilityValue) !== 'string') {
+ currentVisibilityValue = 'visible';
+ }
+
+ let newVisibilityValue: string;
+ if (currentVisibilityValue === 'visible') {
+ newVisibilityValue = 'toggle';
+ } else {
+ newVisibilityValue = 'visible';
+ }
+
+ this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleMenuBarAction.menuBarVisibilityKey, value: newVisibilityValue }).then(null, error => {
+ this.messageService.show(Severity.Error, error);
+ });
+
+ return TPromise.as(null);
}
}
diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts
index 8be72f39727..4ac436381e3 100644
--- a/src/vs/workbench/electron-browser/main.contribution.ts
+++ b/src/vs/workbench/electron-browser/main.contribution.ts
@@ -225,6 +225,15 @@ Note that there can still be cases where this setting is ignored (e.g. when usin
}
};
+if (platform.isWindows || platform.isLinux) {
+ properties['window.menuBarVisibility'] = {
+ 'type': 'string',
+ 'enum': ['visible', 'toggle', 'hidden'],
+ 'default': 'visible',
+ 'description': nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that a single press of the alt key will show and hide the menu bar.")
+ };
+}
+
if (platform.isWindows) {
properties['window.autoDetectHighContrast'] = {
'type': 'boolean',
@@ -273,4 +282,4 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('zenMode.hideStatusBar', "Controls if turning on Zen Mode also hides the status bar at the bottom of the workbench.")
}
}
-});
+});
\ No newline at end of file
diff --git a/src/vs/workbench/node/extensionPoints.ts b/src/vs/workbench/node/extensionPoints.ts
index 6957846b25d..4da349b2266 100644
--- a/src/vs/workbench/node/extensionPoints.ts
+++ b/src/vs/workbench/node/extensionPoints.ts
@@ -84,15 +84,11 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
public parse(): TPromise {
return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => {
- let errors: json.ParseError[] = [];
- const extensionDescription = json.parse(manifestContents.toString(), errors);
- if (errors.length > 0) {
- errors.forEach((error) => {
- this._collector.error(this._absoluteFolderPath, nls.localize('jsonParseFail', "Failed to parse {0}: {1}.", this._absoluteManifestPath, json.getParseErrorMessage(error.error)));
- });
- return null;
+ try {
+ return JSON.parse(manifestContents.toString());
+ } catch (e) {
+ this._collector.error(this._absoluteFolderPath, nls.localize('jsonParseFail', "Failed to parse {0}: {1}.", this._absoluteManifestPath, json.getParseErrorMessage(e.message)));
}
- return extensionDescription;
}, (err) => {
if (err.code === 'ENOENT') {
return null;
@@ -165,40 +161,44 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
/**
* This routine make the following assumptions:
* The root element is a object literal
- * Strings to replace are one values of a key. So for example string[] are ignored.
- * This is done to speed things up.
*/
private static _replaceNLStrings(literal: T, messages: { [key: string]: string; }, collector: MessagesCollector, messageScope: string): void {
- Object.keys(literal).forEach(key => {
- if (literal.hasOwnProperty(key)) {
- let value = literal[key];
- if (Types.isString(value)) {
- let str = value;
- let length = str.length;
- if (length > 1 && str[0] === '%' && str[length - 1] === '%') {
- let messageKey = str.substr(1, length - 2);
- let message = messages[messageKey];
- if (message) {
- if (nlsConfig.pseudo) {
- // FF3B and FF3D is the Unicode zenkaku representation for [ and ]
- message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D';
- }
- literal[key] = message;
- } else {
- collector.warn(messageScope, nls.localize('missingNLSKey', "Couldn't find message for key {0}.", messageKey));
+ function processEntry(obj: any, key: string | number) {
+ let value = obj[key];
+ if (Types.isString(value)) {
+ let str = value;
+ let length = str.length;
+ if (length > 1 && str[0] === '%' && str[length - 1] === '%') {
+ let messageKey = str.substr(1, length - 2);
+ let message = messages[messageKey];
+ if (message) {
+ if (nlsConfig.pseudo) {
+ // FF3B and FF3D is the Unicode zenkaku representation for [ and ]
+ message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D';
}
+ obj[key] = message;
+ } else {
+ collector.warn(messageScope, nls.localize('missingNLSKey', "Couldn't find message for key {0}.", messageKey));
}
- } else if (Types.isObject(value)) {
- ExtensionManifestNLSReplacer._replaceNLStrings(value, messages, collector, messageScope);
- } else if (Types.isArray(value)) {
- (value).forEach(element => {
- if (Types.isObject(element)) {
- ExtensionManifestNLSReplacer._replaceNLStrings(element, messages, collector, messageScope);
- }
- });
+ }
+ } else if (Types.isObject(value)) {
+ for (let k in value) {
+ if (value.hasOwnProperty(k)) {
+ processEntry(value, k);
+ }
+ }
+ } else if (Types.isArray(value)) {
+ for (let i = 0; i < value.length; i++) {
+ processEntry(value, i);
}
}
- });
+ }
+
+ for (let key in literal) {
+ if (literal.hasOwnProperty(key)) {
+ processEntry(literal, key);
+ }
+ };
}
}
diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts
index 9a6b23de05b..5b97853ef25 100644
--- a/src/vs/workbench/parts/debug/common/debugModel.ts
+++ b/src/vs/workbench/parts/debug/common/debugModel.ts
@@ -520,7 +520,7 @@ export class Process implements debug.IProcess {
thread.stopped = true;
thread.clearCallStack();
});
- } else {
+ } else if (this.threads.has(data.threadId)) {
// One thread is stopped, only update that thread.
const thread = this.threads.get(data.threadId);
thread.stoppedDetails = data.stoppedDetails;
diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts
index 2cda12aa0b4..64e21cd4d8d 100644
--- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts
+++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts
@@ -580,11 +580,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
.then(null, onUnexpectedError);
}
}
- }
-
- if (extension.gallery) {
- // Report telemetry only for gallery extensions
- this.reportTelemetry(installing, !error);
+ if (extension.gallery) {
+ // Report telemetry only for gallery extensions
+ this.reportTelemetry(installing, !error);
+ }
}
this._onChange.fire();
}
diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts
index 623df36d1cb..8ded8efe06a 100644
--- a/src/vs/workbench/parts/files/browser/fileActions.ts
+++ b/src/vs/workbench/parts/files/browser/fileActions.ts
@@ -46,6 +46,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { Keybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
+import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
+import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
export interface IEditableData {
action: IAction;
@@ -271,7 +273,9 @@ class RenameFileAction extends BaseRenameAction {
element: FileStat,
@IFileService fileService: IFileService,
@IMessageService messageService: IMessageService,
- @ITextFileService textFileService: ITextFileService
+ @ITextFileService textFileService: ITextFileService,
+ @ITextModelResolverService private textModelResolverService: ITextModelResolverService,
+ @IBackupFileService private backupFileService: IBackupFileService
) {
super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, fileService, messageService, textFileService);
@@ -280,40 +284,44 @@ class RenameFileAction extends BaseRenameAction {
public runAction(newName: string): TPromise {
- // Handle dirty
- let revertPromise: TPromise = TPromise.as(null);
+ // 1. check for dirty files that are being moved and backup to new target
const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, this.element.resource.fsPath));
- if (dirty.length) {
- let message: string;
- if (this.element.isDirectory) {
- if (dirty.length === 1) {
- message = nls.localize('dirtyMessageFolderOne', "You are renaming a folder with unsaved changes in 1 file. Do you want to continue?");
- } else {
- message = nls.localize('dirtyMessageFolder', "You are renaming a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length);
- }
- } else {
- message = nls.localize('dirtyMessageFile', "You are renaming a file with unsaved changes. Do you want to continue?");
+ const dirtyRenamed: URI[] = [];
+ return TPromise.join(dirty.map(d => {
+ const targetPath = paths.join(this.element.parent.resource.fsPath, newName);
+ let renamed: URI;
+
+ // If the dirty file itself got moved, just reparent it to the target folder
+ if (this.element.resource.fsPath === d.fsPath) {
+ renamed = URI.file(targetPath);
}
- const res = this.messageService.confirm({
- message,
- type: 'warning',
- detail: nls.localize('dirtyWarning', "Your changes will be lost if you don't save them."),
- primaryButton: nls.localize({ key: 'renameLabel', comment: ['&& denotes a mnemonic'] }, "&&Rename")
- });
-
- if (!res) {
- return TPromise.as(null);
+ // Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example:
+ else {
+ renamed = URI.file(paths.join(targetPath, d.fsPath.substr(this.element.resource.fsPath.length + 1)));
}
- revertPromise = this.textFileService.revertAll(dirty);
- }
+ dirtyRenamed.push(renamed);
- return revertPromise.then(() => {
- return this.fileService.rename(this.element.resource, newName).then(null, (error: Error) => {
- this.onErrorWithRetry(error, () => this.runAction(newName));
+ const model = this.textFileService.models.get(d);
+
+ return this.backupFileService.backupResource(renamed, model.getValue(), model.getVersionId());
+ }))
+
+ // 2. soft revert all dirty since we have backed up their contents
+ .then(() => this.textFileService.revertAll(dirty, { soft: true /* do not attempt to load content from disk */ }))
+
+ // 3.) run the rename operation
+ .then(() => this.fileService.rename(this.element.resource, newName).then(null, (error: Error) => {
+ return TPromise.join(dirtyRenamed.map(d => this.backupFileService.discardResourceBackup(d))).then(() => {
+ this.onErrorWithRetry(error, () => this.runAction(newName));
+ });
+ }))
+
+ // 4.) resolve those that were dirty to load their previous dirty contents from disk
+ .then(() => {
+ return TPromise.join(dirtyRenamed.map(t => this.textModelResolverService.createModelReference(t)));
});
- });
}
}
@@ -821,12 +829,23 @@ export class ImportFileAction extends BaseFileAction {
// Run import in sequence
const importPromisesFactory: ITask>[] = [];
- filesArray.forEach((file) => {
+ filesArray.forEach(file => {
importPromisesFactory.push(() => {
const sourceFile = URI.file(file.path);
+ const targetFile = URI.file(paths.join(targetElement.resource.fsPath, paths.basename(file.path)));
- return this.fileService.importFile(sourceFile, targetElement.resource).then(null, (error: any) => {
- this.messageService.show(Severity.Error, error);
+ // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents
+ // of the target file would replace the contents of the imported file. since we already
+ // confirmed the overwrite before, this is OK.
+ let revertPromise = TPromise.as(null);
+ if (this.textFileService.isDirty(targetFile)) {
+ revertPromise = this.textFileService.revertAll([targetFile], { soft: true });
+ }
+
+ return revertPromise.then(() => {
+ return this.fileService.importFile(sourceFile, targetElement.resource).then(null, (error: any) => {
+ this.messageService.show(Severity.Error, error);
+ });
});
});
});
diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts
index 896f5c17413..c7bc7fd1155 100644
--- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts
+++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts
@@ -49,6 +49,8 @@ import { Keybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
+import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
+import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
export class FileDataSource implements IDataSource {
constructor(
@@ -723,7 +725,9 @@ export class FileDragAndDrop implements IDragAndDrop {
@IFileService private fileService: IFileService,
@IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService private instantiationService: IInstantiationService,
- @ITextFileService private textFileService: ITextFileService
+ @ITextFileService private textFileService: ITextFileService,
+ @ITextModelResolverService private textModelResolverService: ITextModelResolverService,
+ @IBackupFileService private backupFileService: IBackupFileService
) {
this.toDispose = [];
@@ -868,70 +872,87 @@ export class FileDragAndDrop implements IDragAndDrop {
promise = tree.expand(target).then(() => {
- // Reuse action if user copies
+ // Reuse duplicate action if user copies
if (isCopy) {
- const copyAction = this.instantiationService.createInstance(DuplicateFileAction, tree, source, target);
- return copyAction.run();
+ return this.instantiationService.createInstance(DuplicateFileAction, tree, source, target).run();
}
- // Handle dirty (in file or inside the folder if any)
- let revertPromise: TPromise = TPromise.as(null);
+ const dirtyMoved: URI[] = [];
+
+ // Success: load all files that are dirty again to restore their dirty contents
+ // Error: discard any backups created during the process
+ const onSuccess = () => TPromise.join(dirtyMoved.map(t => this.textModelResolverService.createModelReference(t)));
+ const onError = (error?: Error, showError?: boolean) => {
+ if (showError) {
+ this.messageService.show(Severity.Error, error);
+ }
+
+ return TPromise.join(dirtyMoved.map(d => this.backupFileService.discardResourceBackup(d)));
+ };
+
+ // 1. check for dirty files that are being moved and backup to new target
const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, source.resource.fsPath));
- if (dirty.length) {
- let message: string;
- if (source.isDirectory) {
- if (dirty.length === 1) {
- message = nls.localize('dirtyMessageFolderOne', "You are moving a folder with unsaved changes in 1 file. Do you want to continue?");
- } else {
- message = nls.localize('dirtyMessageFolder', "You are moving a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length);
- }
- } else {
- message = nls.localize('dirtyMessageFile', "You are moving a file with unsaved changes. Do you want to continue?");
+ return TPromise.join(dirty.map(d => {
+ let moved: URI;
+
+ // If the dirty file itself got moved, just reparent it to the target folder
+ if (source.resource.fsPath === d.fsPath) {
+ moved = URI.file(paths.join(target.resource.fsPath, source.name));
}
- const res = this.messageService.confirm({
- message,
- type: 'warning',
- detail: nls.localize('dirtyWarning', "Your changes will be lost if you don't save them."),
- primaryButton: nls.localize({ key: 'moveLabel', comment: ['&& denotes a mnemonic'] }, "&&Move")
- });
-
- if (!res) {
- return TPromise.as(null);
+ // Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example:
+ else {
+ moved = URI.file(paths.join(target.resource.fsPath, d.fsPath.substr(source.parent.resource.fsPath.length + 1)));
}
- revertPromise = this.textFileService.revertAll(dirty);
- }
+ dirtyMoved.push(moved);
- return revertPromise.then(() => {
- const targetResource = URI.file(paths.join(target.resource.fsPath, source.name));
- let didHandleConflict = false;
+ const model = this.textFileService.models.get(d);
- // Move File/Folder
- return this.fileService.moveFile(source.resource, targetResource).then(null, error => {
+ return this.backupFileService.backupResource(moved, model.getValue(), model.getVersionId());
+ }))
- // Conflict
- if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) {
- didHandleConflict = true;
+ // 2. soft revert all dirty since we have backed up their contents
+ .then(() => this.textFileService.revertAll(dirty, { soft: true /* do not attempt to load content from disk */ }))
- const confirm: IConfirmation = {
- message: nls.localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name),
- detail: nls.localize('irreversible', "This action is irreversible!"),
- primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace")
- };
+ // 3.) run the move operation
+ .then(() => {
+ const targetResource = URI.file(paths.join(target.resource.fsPath, source.name));
+ let didHandleConflict = false;
- if (this.messageService.confirm(confirm)) {
- return this.fileService.moveFile(source.resource, targetResource, true).then(null, (error) => {
- this.messageService.show(Severity.Error, error);
- });
+ return this.fileService.moveFile(source.resource, targetResource).then(null, error => {
+
+ // Conflict
+ if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) {
+ didHandleConflict = true;
+
+ const confirm: IConfirmation = {
+ message: nls.localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name),
+ detail: nls.localize('irreversible', "This action is irreversible!"),
+ primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace")
+ };
+
+ // Move with overwrite if the user confirms
+ if (this.messageService.confirm(confirm)) {
+ const targetDirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, targetResource.fsPath));
+
+ // Make sure to revert all dirty in target first to be able to overwrite properly
+ return this.textFileService.revertAll(targetDirty, { soft: true /* do not attempt to load content from disk */ }).then(() => {
+
+ // Then continue to do the move operation
+ return this.fileService.moveFile(source.resource, targetResource, true).then(onSuccess, error => onError(error, true));
+ });
+ }
+
+ return onError();
}
- return;
- }
+ return onError(error, true);
+ });
+ })
- this.messageService.show(Severity.Error, error);
- });
- });
+ // 4.) resolve those that were dirty to load their previous dirty contents from disk
+ .then(onSuccess, onError);
}, errors.onUnexpectedError);
}
diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts
index 2e07718f129..7e5a6325049 100644
--- a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts
+++ b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts
@@ -8,9 +8,9 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import errors = require('vs/base/common/errors');
import URI from 'vs/base/common/uri';
import paths = require('vs/base/common/paths');
-import { IEditor } from 'vs/editor/common/editorCommon';
+import { IEditor, IEditorViewState, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
import { IEditor as IBaseEditor } from 'vs/platform/editor/common/editor';
-import { EditorInput, IEditorStacksModel, SideBySideEditorInput } from 'vs/workbench/common/editor';
+import { toResource, EditorInput, IEditorStacksModel, SideBySideEditorInput, IEditorGroup } from 'vs/workbench/common/editor';
import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files';
import { ITextFileService, ModelState } from 'vs/workbench/services/textfile/common/textfiles';
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
@@ -157,13 +157,42 @@ export class FileEditorTracker implements IWorkbenchContribution {
}
// Reopen
- this.editorService.openEditor({ resource: reopenFileResource, options: { preserveFocus: true, pinned: group.isPinned(input), index: group.indexOf(input), inactive: !group.isActive(input) } }, stacks.positionOfGroup(group)).done(null, errors.onUnexpectedError);
+ this.editorService.openEditor({
+ resource: reopenFileResource,
+ options: {
+ preserveFocus: true,
+ pinned: group.isPinned(input),
+ index: group.indexOf(input),
+ inactive: !group.isActive(input),
+ viewState: this.getViewStateFor(oldResource, group)
+ }
+ }, stacks.positionOfGroup(group)).done(null, errors.onUnexpectedError);
}
}
});
});
}
+ private getViewStateFor(resource: URI, group: IEditorGroup): IEditorViewState {
+ const stacks = this.editorGroupService.getStacksModel();
+ const editors = this.editorService.getVisibleEditors();
+
+ for (let i = 0; i < editors.length; i++) {
+ const editor = editors[i];
+ if (editor && editor.position === stacks.positionOfGroup(group)) {
+ const resource = toResource(editor.input, { filter: 'file' });
+ if (resource && resource.fsPath === resource.fsPath) {
+ const control = editor.getControl();
+ if (isCommonCodeEditor(control)) {
+ return control.saveViewState();
+ }
+ }
+ }
+ }
+
+ return void 0;
+ }
+
private handleUpdatesToVisibleEditors(e: FileChangesEvent) {
const editors = this.editorService.getVisibleEditors();
editors.forEach(editor => {
diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts
index bc7a744c14f..96c0a7da7ba 100644
--- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts
+++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts
@@ -142,7 +142,6 @@ export class DefaultPreferencesEditor extends BaseTextEditor {
options.folding = false;
options.renderWhitespace = 'none';
options.wrappingColumn = 0;
- options.overviewRulerLanes = 0;
options.renderIndentGuides = false;
options.rulers = [];
}
diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts
index f3e53ff1119..24d871f3a58 100644
--- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts
+++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts
@@ -125,7 +125,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
openWorkspaceSettings(): TPromise {
if (!this.contextService.hasWorkspace()) {
this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings"));
- return;
+ return TPromise.as(null);
}
return this.openSettings(ConfigurationTarget.WORKSPACE);
}
diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts
index 8bbd3b3911d..a6260610aec 100644
--- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts
+++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts
@@ -91,8 +91,11 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone {
}
private layout(): void {
- this.titleContainer.style.lineHeight = this.editor.getConfiguration().lineHeight + 3 + 'px';
- this.titleContainer.style.fontSize = this.editor.getConfiguration().fontInfo.fontSize + 'px';
+ const configuration = this.editor.getConfiguration();
+ const layoutInfo = this.editor.getLayoutInfo();
+ this.titleContainer.style.width = layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth + 'px';
+ this.titleContainer.style.lineHeight = configuration.lineHeight + 3 + 'px';
+ this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px';
const iconSize = this.getIconSize();
this.icon.style.height = `${iconSize}px`;
this.icon.style.width = `${iconSize}px`;
diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts
index 748b1904ff2..1fcd97fd773 100644
--- a/src/vs/workbench/parts/search/browser/searchViewlet.ts
+++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts
@@ -470,6 +470,7 @@ export class SearchViewlet extends Viewlet {
}
// Reveal the newly selected element
+ this.tree.setFocus(next, eventPayload);
this.tree.setSelection([next], eventPayload);
this.tree.reveal(next);
}
@@ -497,7 +498,9 @@ export class SearchViewlet extends Viewlet {
}
// Reveal the newly selected element
+ this.tree.setFocus(prev, eventPayload);
this.tree.setSelection([prev], eventPayload);
+ this.tree.reveal(prev);
}
public setVisible(visible: boolean): TPromise {
diff --git a/src/vs/workbench/parts/tasks/test/node/configuration.test.ts b/src/vs/workbench/parts/tasks/test/node/configuration.test.ts
index 90ec0957c87..2cd2709a8d8 100644
--- a/src/vs/workbench/parts/tasks/test/node/configuration.test.ts
+++ b/src/vs/workbench/parts/tasks/test/node/configuration.test.ts
@@ -229,11 +229,6 @@ class PatternBuilder {
this.result.loop = value;
return this;
}
-
- public mostSignifikant(value: boolean): PatternBuilder {
- this.result.mostSignifikant = value;
- return this;
- }
}
@@ -1007,6 +1002,5 @@ suite('Tasks Configuration parsing tests', () => {
assert.strictEqual(actual.code, expected.code);
assert.strictEqual(actual.severity, expected.severity);
assert.strictEqual(actual.loop, expected.loop);
- assert.strictEqual(actual.mostSignifikant, expected.mostSignifikant);
}
});
\ No newline at end of file
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts
index 113024a23db..7d05f7e29aa 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IConfiguration, DefaultConfig } from 'vs/editor/common/config/defaultConfig';
+import { IConfiguration as IEditorConfiguration, DefaultConfig } from 'vs/editor/common/config/defaultConfig';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShell } from 'vs/workbench/parts/terminal/common/terminal';
import { Platform } from 'vs/base/common/platform';
@@ -91,16 +91,16 @@ export class TerminalConfigHelper implements ITerminalConfigHelper {
this._charMeasureElement = document.createElement('div');
this.panelContainer.appendChild(this._charMeasureElement);
}
- let style = this._charMeasureElement.style;
+ const style = this._charMeasureElement.style;
style.display = 'block';
style.fontFamily = fontFamily;
style.fontSize = fontSize + 'px';
- style.height = Math.floor(lineHeight * fontSize) + 'px';
+ style.lineHeight = lineHeight.toString(10);
this._charMeasureElement.innerText = 'X';
- let rect = this._charMeasureElement.getBoundingClientRect();
+ const rect = this._charMeasureElement.getBoundingClientRect();
style.display = 'none';
- let charWidth = Math.ceil(rect.width);
- let charHeight = Math.ceil(rect.height);
+ const charWidth = rect.width;
+ const charHeight = rect.height;
return {
fontFamily,
fontSize: fontSize + 'px',
@@ -115,37 +115,46 @@ export class TerminalConfigHelper implements ITerminalConfigHelper {
* terminal.integrated.fontSize, terminal.integrated.lineHeight configuration properties
*/
public getFont(): ITerminalFont {
- let terminalConfig = this._configurationService.getConfiguration().terminal.integrated;
- let editorConfig = this._configurationService.getConfiguration();
+ const config = this._configurationService.getConfiguration();
+ const editorConfig = (config).editor;
+ const terminalConfig = (config).terminal.integrated;
- let fontFamily = terminalConfig.fontFamily || editorConfig.editor.fontFamily;
+ const fontFamily = terminalConfig.fontFamily || editorConfig.fontFamily;
let fontSize = this._toInteger(terminalConfig.fontSize, 0);
if (fontSize <= 0) {
fontSize = DefaultConfig.editor.fontSize;
}
let lineHeight = terminalConfig.lineHeight <= 0 ? DEFAULT_LINE_HEIGHT : terminalConfig.lineHeight;
+ if (!lineHeight) {
+ lineHeight = DEFAULT_LINE_HEIGHT;
+ }
return this._measureFont(fontFamily, fontSize, lineHeight);
}
public getFontLigaturesEnabled(): boolean {
- let terminalConfig = this._configurationService.getConfiguration().terminal.integrated;
- return terminalConfig.fontLigatures;
+ const terminalConfig = this._configurationService.getConfiguration();
+ return terminalConfig.terminal.integrated.fontLigatures;
}
public getCursorBlink(): boolean {
- let terminalConfig = this._configurationService.getConfiguration().terminal.integrated;
- return terminalConfig.cursorBlinking;
+ const terminalConfig = this._configurationService.getConfiguration();
+ return terminalConfig.terminal.integrated.cursorBlinking;
}
public getRightClickCopyPaste(): boolean {
- let config = this._configurationService.getConfiguration();
+ const config = this._configurationService.getConfiguration();
return config.terminal.integrated.rightClickCopyPaste;
}
+ public getCommandsToSkipShell(): string[] {
+ const config = this._configurationService.getConfiguration();
+ return config.terminal.integrated.commandsToSkipShell;
+ }
+
public getShell(): IShell {
- let config = this._configurationService.getConfiguration();
- let shell: IShell = {
+ const config = this._configurationService.getConfiguration();
+ const shell: IShell = {
executable: '',
args: []
};
@@ -190,9 +199,4 @@ export class TerminalConfigHelper implements ITerminalConfigHelper {
}
return r;
}
-
- public getCommandsToSkipShell(): string[] {
- let config = this._configurationService.getConfiguration();
- return config.terminal.integrated.commandsToSkipShell;
- }
}
\ No newline at end of file
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts
index 2e56d1240c9..f2b76b3f41c 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts
@@ -156,8 +156,8 @@ export class TerminalInstance implements ITerminalInstance {
}, 0);
});
- let xtermHelper: HTMLElement = this._xterm.element.querySelector('.xterm-helpers');
- let focusTrap: HTMLElement = document.createElement('div');
+ const xtermHelper: HTMLElement = this._xterm.element.querySelector('.xterm-helpers');
+ const focusTrap: HTMLElement = document.createElement('div');
focusTrap.setAttribute('tabindex', '0');
DOM.addClass(focusTrap, 'focus-trap');
focusTrap.addEventListener('focus', function (event: FocusEvent) {
@@ -165,7 +165,7 @@ export class TerminalInstance implements ITerminalInstance {
while (!DOM.hasClass(currentElement, 'part')) {
currentElement = currentElement.parentElement;
}
- let hidePanelElement = currentElement.querySelector('.hide-panel-action');
+ const hidePanelElement = currentElement.querySelector('.hide-panel-action');
hidePanelElement.focus();
});
xtermHelper.insertBefore(focusTrap, this._xterm.textarea);
@@ -240,7 +240,7 @@ export class TerminalInstance implements ITerminalInstance {
if (!this._xterm) {
return;
}
- let text = window.getSelection().toString();
+ const text = window.getSelection().toString();
if (!text || force) {
this._xterm.focus();
}
@@ -331,11 +331,11 @@ export class TerminalInstance implements ITerminalInstance {
}
protected _createProcess(workspace: IWorkspace, name: string, shell: IShell) {
- let locale = this._configHelper.isSetLocaleVariables() ? platform.locale : undefined;
+ const locale = this._configHelper.isSetLocaleVariables() ? platform.locale : undefined;
if (!shell.executable) {
shell = this._configHelper.getShell();
}
- let env = TerminalInstance.createTerminalEnv(process.env, shell, this._getCwd(workspace, shell.ignoreCustomCwd), locale);
+ const env = TerminalInstance.createTerminalEnv(process.env, shell, this._getCwd(workspace, shell.ignoreCustomCwd), locale);
this._title = name ? name : '';
this._process = cp.fork('./terminalProcess', [], {
env: env,
@@ -378,7 +378,7 @@ export class TerminalInstance implements ITerminalInstance {
// TODO: This should be private/protected
// TODO: locale should not be optional
public static createTerminalEnv(parentEnv: IStringDictionary, shell: IShell, cwd: string, locale?: string): IStringDictionary {
- let env = TerminalInstance._cloneEnv(parentEnv);
+ const env = TerminalInstance._cloneEnv(parentEnv);
env['PTYPID'] = process.pid.toString();
env['PTYSHELL'] = shell.executable;
if (shell.args) {
@@ -402,7 +402,7 @@ export class TerminalInstance implements ITerminalInstance {
}
private static _cloneEnv(env: IStringDictionary): IStringDictionary {
- let newEnv: IStringDictionary = Object.create(null);
+ const newEnv: IStringDictionary = Object.create(null);
Object.keys(env).forEach((key) => {
newEnv[key] = env[key];
});
@@ -442,7 +442,7 @@ export class TerminalInstance implements ITerminalInstance {
}
public layout(dimension: { width: number, height: number }): void {
- let font = this._configHelper.getFont();
+ const font = this._configHelper.getFont();
if (!font || !font.charWidth || !font.charHeight) {
return;
}
@@ -455,10 +455,12 @@ export class TerminalInstance implements ITerminalInstance {
// Upstream issue: https://github.com/sourcelair/xterm.js/issues/291
this._xterm.emit('scroll', this._xterm.ydisp);
}
- let leftPadding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10);
- let innerWidth = dimension.width - leftPadding;
- let cols = Math.floor(innerWidth / font.charWidth);
- let rows = Math.floor(dimension.height / font.charHeight);
+ const padding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10);
+ // Use left padding as right padding, right padding is not defined in CSS just in case
+ // xterm.js causes an unexpected overflow.
+ const innerWidth = dimension.width - padding * 2;
+ const cols = Math.floor(innerWidth / font.charWidth);
+ const rows = Math.floor(dimension.height / font.charHeight);
if (this._xterm) {
this._xterm.resize(cols, rows);
this._xterm.element.style.width = innerWidth + 'px';
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js b/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js
index c6188f790b9..c1cadb0e30d 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js
@@ -6,7 +6,7 @@
var fs = require('fs');
var os = require('os');
var path = require('path');
-var ptyJs = require('pty.js');
+var ptyJs = require('node-pty');
// The pty process needs to be run in its own child process to get around maxing out CPU on Mac,
// see https://github.com/electron/electron/issues/38
diff --git a/src/vs/workbench/test/node/api/extHostDocuments.test.ts b/src/vs/workbench/test/node/api/extHostDocuments.test.ts
index b20a73865ef..5f66d6a8947 100644
--- a/src/vs/workbench/test/node/api/extHostDocuments.test.ts
+++ b/src/vs/workbench/test/node/api/extHostDocuments.test.ts
@@ -207,7 +207,7 @@ suite('ExtHostDocument', () => {
test('getWordRangeAtPosition', function () {
data = new ExtHostDocumentData(undefined, URI.file(''), [
- 'aaaa bbbb cccc abc'
+ 'aaaa bbbb+cccc abc'
], '\n', 'text', 1, false);
let range = data.getWordRangeAtPosition(new Position(0, 2));
@@ -216,19 +216,20 @@ suite('ExtHostDocument', () => {
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 4);
+ // ignore bad regular expresson /.*/
range = data.getWordRangeAtPosition(new Position(0, 2), /.*/);
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 0);
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 4);
- range = data.getWordRangeAtPosition(new Position(0, 2), /a+.+?c/);
+ range = data.getWordRangeAtPosition(new Position(0, 5), /[a-z+]+/);
assert.equal(range.start.line, 0);
- assert.equal(range.start.character, 0);
+ assert.equal(range.start.character, 5);
assert.equal(range.end.line, 0);
- assert.equal(range.end.character, 11);
+ assert.equal(range.end.character, 14);
- range = data.getWordRangeAtPosition(new Position(0, 17), /a+.+?c/);
+ range = data.getWordRangeAtPosition(new Position(0, 17), /[a-z+]+/);
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 15);
assert.equal(range.end.line, 0);
diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts
index 4321cd6487b..22652887145 100644
--- a/src/vs/workbench/test/workbenchTestServices.ts
+++ b/src/vs/workbench/test/workbenchTestServices.ts
@@ -774,10 +774,6 @@ export class TestWindowService implements IWindowService {
return TPromise.as(void 0);
}
- toggleMenuBar(): TPromise {
- return TPromise.as(void 0);
- }
-
isMaximized(): TPromise {
return TPromise.as(void 0);
}
@@ -879,9 +875,6 @@ export class TestWindowsService implements IWindowsService {
setDocumentEdited(windowId: number, flag: boolean): TPromise {
return TPromise.as(void 0);
}
- toggleMenuBar(windowId: number): TPromise {
- return TPromise.as(void 0);
- }
quit(): TPromise {
return TPromise.as(void 0);
}
@@ -923,4 +916,4 @@ export class TestWindowsService implements IWindowsService {
startCrashReporter(config: Electron.CrashReporterStartOptions): TPromise {
return TPromise.as(void 0);
}
-}
\ No newline at end of file
+}