diff --git a/.travis.yml b/.travis.yml
index 02d0fb00dcc..41c1e2c94d7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,8 +40,8 @@ install:
script:
- gulp hygiene --silent
- gulp electron --silent
- - gulp compile --silent
- - gulp optimize-vscode --silent
+ - gulp compile --silent --max_old_space_size=4096
+ - gulp optimize-vscode --silent --max_old_space_size=4096
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./scripts/test.sh --reporter dot --coverage; else ./scripts/test.sh --reporter dot; fi
- ./scripts/test-integration.sh
diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage
index 754d0179fb6..38c37bd25f7 100644
--- a/extensions/markdown/syntaxes/markdown.tmLanguage
+++ b/extensions/markdown/syntaxes/markdown.tmLanguage
@@ -48,10 +48,6 @@
include
-
-
-
-
#fenced_code_block_css
@@ -126,6 +122,10 @@
include
#fenced_code_block_c
+
+ include
+ #fenced_code_block_cpp
+
include
#fenced_code_block_diff
@@ -252,7 +252,7 @@
heading
begin
- (?:^|\G)(#{1,6})\s*(?=[\S[^#]])
+ (?:^|\G)[ ]{0,3}(#{1,6})\s*(?=[\S[^#]])
captures
1
@@ -563,10 +563,7 @@
while
-
-
-
- (^|\G)(?!\s*$|#|[ ]{0,3}((([*_][ ]{0,2}\2){2,}[ \t]*$\n?)|([*+-]([ ]{1,3}|\t)))|\s*\[.+?\]:|>)
+ (^|\G)((?=\s*[-=]{3,}\s*$)|[ ]{4,}(?=\S))
fenced_code_block_css
@@ -964,7 +961,6 @@
punctuation.definition.markdown
-
patterns
@@ -1560,7 +1556,6 @@
punctuation.definition.markdown
-
patterns
@@ -1578,6 +1573,57 @@
+ fenced_code_block_cpp
+
+ begin
+ (^|\G)(\s*)([`~]{3,})\s*((cpp|c\+\+|cxx)(\s+.*)?$)
+ name
+ markup.fenced_code.block.markdown
+ end
+ (^|\G)(\2|\s{0,3})(\3)\s*$
+ beginCaptures
+
+ 3
+
+ name
+ punctuation.definition.markdown
+
+ 5
+
+ name
+ fenced_code.block.language
+
+ 6
+
+ name
+ fenced_code.block.language.attributes
+
+
+ endCaptures
+
+ 3
+
+ name
+ punctuation.definition.markdown
+
+
+ patterns
+
+
+ begin
+ (^|\G)(\s*)(.*)
+ while
+ (^|\G)(?!\s*([`~]{3,})\s*$)
+ patterns
+
+
+ include
+ source.cpp
+
+
+
+
+
fenced_code_block_diff
begin
diff --git a/extensions/typescript/src/features/completionItemProvider.ts b/extensions/typescript/src/features/completionItemProvider.ts
index 0f1c282d850..499e0eff94f 100644
--- a/extensions/typescript/src/features/completionItemProvider.ts
+++ b/extensions/typescript/src/features/completionItemProvider.ts
@@ -198,7 +198,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
const preText = document.getText(new Range(
new Position(position.line, 0),
new Position(position.line, position.character - 1)));
- enableDotCompletions = preText.match(/[a-z_$]\s*$/ig) !== null;
+ enableDotCompletions = preText.match(/[a-z_$\)\]\}]\s*$/ig) !== null;
}
for (let i = 0; i < body.length; i++) {
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 8d541e33c20..3f293d99d29 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -333,9 +333,9 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
},
"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",
+ "version": "0.6.0-beta2",
+ "from": "node-pty@0.6.0-beta2",
+ "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.6.0-beta2.tgz",
"dependencies": {
"extend": {
"version": "1.2.1",
@@ -432,7 +432,7 @@
"xterm": {
"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#36c63323c3f940636e799ae6e0168b2dfd7a3d21"
+ "resolved": "git+https://github.com/Tyriar/xterm.js.git#745a40e15a2a6387df56340625e3c504e84826cf"
},
"yauzl": {
"version": "2.3.1",
diff --git a/package.json b/package.json
index 3b4dca5b90f..58b7ea64d49 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",
- "node-pty": "0.4.1",
+ "node-pty": "0.6.0-beta2",
"semver": "4.3.6",
"vscode-debugprotocol": "1.15.0",
"vscode-textmate": "3.1.0",
diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh
index 1281a1f8c38..251d718cfee 100644
--- a/resources/win32/bin/code.sh
+++ b/resources/win32/bin/code.sh
@@ -6,6 +6,14 @@
NAME="@@NAME@@"
VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")"
ELECTRON="$VSCODE_PATH/$NAME.exe"
+if grep -q Microsoft /proc/version; then
+ # If running under WSL don't pass cli.js to Electron as environment vars
+ # cannot be transferred from WSL to Windows
+ # See: https://github.com/Microsoft/BashOnWindows/issues/1363
+ # https://github.com/Microsoft/BashOnWindows/issues/1494
+ "$ELECTRON" "$@"
+ exit $?
+fi
if [ "$(expr substr $(uname -s) 1 9)" == "CYGWIN_NT" ]; then
CLI=$(cygpath -m "$VSCODE_PATH/resources/app/out/cli.js")
else
diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts
index 9a1e56d073b..b9b8d37bb71 100644
--- a/src/vs/base/browser/browser.ts
+++ b/src/vs/base/browser/browser.ts
@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
-import types = require('vs/base/common/types');
import * as Platform from 'vs/base/common/platform';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
@@ -129,37 +128,9 @@ export const canUseTranslate3d = !isFirefox;
export const enableEmptySelectionClipboard = isWebKit;
-/**
- * Returns if the browser supports CSS 3 animations.
- */
-export function hasCSSAnimationSupport() {
- if (this._hasCSSAnimationSupport === true || this._hasCSSAnimationSupport === false) {
- return this._hasCSSAnimationSupport;
- }
-
- let supported = false;
- let element = document.createElement('div');
- let properties = ['animationName', 'webkitAnimationName', 'msAnimationName', 'MozAnimationName', 'OAnimationName'];
- for (let i = 0; i < properties.length; i++) {
- let property = properties[i];
- if (!types.isUndefinedOrNull(element.style[property]) || element.style.hasOwnProperty(property)) {
- supported = true;
- break;
- }
- }
-
- if (supported) {
- this._hasCSSAnimationSupport = true;
- } else {
- this._hasCSSAnimationSupport = false;
- }
-
- return this._hasCSSAnimationSupport;
-}
-
export function supportsExecCommand(command: string): boolean {
return (
(isIE || Platform.isNative)
&& document.queryCommandSupported(command)
);
-}
\ No newline at end of file
+}
diff --git a/src/vs/base/browser/builder.css b/src/vs/base/browser/builder.css
index 1c9feba33d7..9d428896b67 100644
--- a/src/vs/base/browser/builder.css
+++ b/src/vs/base/browser/builder.css
@@ -2,8 +2,8 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-/* ---------- Builder ---------- */
-.hidden {
+
+.builder-hidden {
display: none !important;
visibility: hidden !important;
}
diff --git a/src/vs/base/browser/builder.ts b/src/vs/base/browser/builder.ts
index 4f586a24a2e..0f6307e1d87 100644
--- a/src/vs/base/browser/builder.ts
+++ b/src/vs/base/browser/builder.ts
@@ -1280,8 +1280,8 @@ export class Builder implements IDisposable {
* Shows the current element of the builder.
*/
public show(): Builder {
- if (this.hasClass('hidden')) {
- this.removeClass('hidden');
+ if (this.hasClass('builder-hidden')) {
+ this.removeClass('builder-hidden');
}
this.attr('aria-hidden', 'false');
@@ -1319,8 +1319,8 @@ export class Builder implements IDisposable {
* Hides the current element of the builder.
*/
public hide(): Builder {
- if (!this.hasClass('hidden')) {
- this.addClass('hidden');
+ if (!this.hasClass('builder-hidden')) {
+ this.addClass('builder-hidden');
}
this.attr('aria-hidden', 'true');
@@ -1334,7 +1334,7 @@ export class Builder implements IDisposable {
* Returns true if the current element of the builder is hidden.
*/
public isHidden(): boolean {
- return this.hasClass('hidden') || this.currentElement.style.display === 'none';
+ return this.hasClass('builder-hidden') || this.currentElement.style.display === 'none';
}
/**
@@ -1345,7 +1345,7 @@ export class Builder implements IDisposable {
// Cancel any pending showDelayed() invocation
this.cancelVisibilityPromise();
- this.swapClass('builder-visible', 'hidden');
+ this.swapClass('builder-visible', 'builder-hidden');
if (this.isHidden()) {
this.attr('aria-hidden', 'true');
diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts
index 567e9d85db4..9ac2e1f1dfa 100644
--- a/src/vs/base/browser/dom.ts
+++ b/src/vs/base/browser/dom.ts
@@ -235,21 +235,7 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur
wrapHandler = _wrapAsStandardKeyboardEvent(handler);
}
- node.addEventListener(type, wrapHandler, useCapture || false);
- return {
- dispose: function () {
- if (!wrapHandler) {
- // Already removed
- return;
- }
- node.removeEventListener(type, wrapHandler, useCapture || false);
-
- // Prevent leakers from holding on to the dom node or handler func
- wrapHandler = null;
- node = null;
- handler = null;
- }
- };
+ return addDisposableListener(node, type, wrapHandler, useCapture);
};
export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable {
diff --git a/src/vs/base/browser/ui/progressbar/progressbar.ts b/src/vs/base/browser/ui/progressbar/progressbar.ts
index b110122c37d..70fb8421bc6 100644
--- a/src/vs/base/browser/ui/progressbar/progressbar.ts
+++ b/src/vs/base/browser/ui/progressbar/progressbar.ts
@@ -8,10 +8,8 @@
import 'vs/css!./progressbar';
import { TPromise, ValueCallback } from 'vs/base/common/winjs.base';
import assert = require('vs/base/common/assert');
-import browser = require('vs/base/browser/browser');
import { Builder, $ } from 'vs/base/browser/builder';
import DOM = require('vs/base/browser/dom');
-import uuid = require('vs/base/common/uuid');
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
const css_done = 'done';
@@ -33,7 +31,6 @@ export class ProgressBar {
private bit: HTMLElement;
private totalWork: number;
private animationStopToken: ValueCallback;
- private currentProgressToken: string;
constructor(builder: Builder) {
this.toUnbind = [];
@@ -130,55 +127,9 @@ export class ProgressBar {
this.element.addClass(css_active);
this.element.addClass(css_infinite);
- if (!browser.hasCSSAnimationSupport()) {
-
- // Use a generated token to avoid race conditions from reentrant calls to this function
- let currentProgressToken = uuid.v4().asHex();
- this.currentProgressToken = currentProgressToken;
-
- this.manualInfinite(currentProgressToken);
- }
-
return this;
}
- private manualInfinite(currentProgressToken: string): void {
- this.bit.style.width = '5%';
- this.bit.style.display = 'inherit';
-
- let counter = 0;
- let animationFn: () => void = () => {
- TPromise.timeout(50).then(() => {
-
- // Return if another manualInfinite() call was made
- if (currentProgressToken !== this.currentProgressToken) {
- return;
- }
-
- // Animation done
- else if (this.element.hasClass(css_done)) {
- this.bit.style.display = 'none';
- this.bit.style.left = '0';
- }
-
- // Wait until progress bar becomes visible
- else if (this.element.isHidden()) {
- animationFn();
- }
-
- // Continue Animation until done
- else {
- counter = (counter + 1) % 95;
- this.bit.style.left = counter + '%';
- animationFn();
- }
- });
- };
-
- // Start Animation
- animationFn();
- }
-
/**
* Tells the progress bar the total number of work. Use in combination with workedVal() to let
* the progress bar show the actual progress based on the work that is done.
diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts
index 7a77f168718..ea4209ebee7 100644
--- a/src/vs/base/common/comparers.ts
+++ b/src/vs/base/common/comparers.ts
@@ -7,27 +7,10 @@
import scorer = require('vs/base/common/scorer');
import strings = require('vs/base/common/strings');
-const FileNameMatch = /^([^.]*)(\.(.*))?$/;
+const FileNameComparer = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
export function compareFileNames(one: string, other: string): number {
- let oneMatch = FileNameMatch.exec(one.toLowerCase());
- let otherMatch = FileNameMatch.exec(other.toLowerCase());
-
- let oneName = oneMatch[1] || '';
- let oneExtension = oneMatch[3] || '';
-
- let otherName = otherMatch[1] || '';
- let otherExtension = otherMatch[3] || '';
-
- if (oneName !== otherName) {
- return oneName < otherName ? -1 : 1;
- }
-
- if (oneExtension === otherExtension) {
- return 0;
- }
-
- return oneExtension < otherExtension ? -1 : 1;
+ return FileNameComparer.compare(one || '', other || '');
}
export function compareAnything(one: string, other: string, lookFor: string): number {
diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts
index 04858fafbf3..781725e6e0a 100644
--- a/src/vs/base/common/labels.ts
+++ b/src/vs/base/common/labels.ts
@@ -7,8 +7,8 @@
import URI from 'vs/base/common/uri';
import platform = require('vs/base/common/platform');
import types = require('vs/base/common/types');
-import strings = require('vs/base/common/strings');
-import paths = require('vs/base/common/paths');
+import { nativeSep, isEqualOrParent, normalize } from 'vs/base/common/paths';
+import { endsWith, ltrim } from 'vs/base/common/strings';
export interface ILabelProvider {
@@ -44,19 +44,19 @@ export function getPathLabel(resource: URI | string, basePathProvider?: URI | st
const basepath = basePathProvider && getPath(basePathProvider);
- if (basepath && paths.isEqualOrParent(absolutePath, basepath)) {
+ if (basepath && isEqualOrParent(absolutePath, basepath)) {
if (basepath === absolutePath) {
return ''; // no label if pathes are identical
}
- return paths.normalize(strings.ltrim(absolutePath.substr(basepath.length), paths.nativeSep), true);
+ return normalize(ltrim(absolutePath.substr(basepath.length), nativeSep), true);
}
if (platform.isWindows && absolutePath && absolutePath[1] === ':') {
- return paths.normalize(absolutePath.charAt(0).toUpperCase() + absolutePath.slice(1), true); // convert c:\something => C:\something
+ return normalize(absolutePath.charAt(0).toUpperCase() + absolutePath.slice(1), true); // convert c:\something => C:\something
}
- return paths.normalize(absolutePath, true);
+ return normalize(absolutePath, true);
}
function getPath(arg1: URI | string | IWorkspaceProvider): string {
@@ -74,4 +74,61 @@ function getPath(arg1: URI | string | IWorkspaceProvider): string {
}
return (arg1).fsPath;
+}
+
+/**
+ * Shortens the paths but keeps them easy to distinguish.
+ * Replaces not important parts with ellipsis.
+ * Every shorten path matches only one original path and vice versa.
+ */
+export function shorten(paths: string[]): string[] {
+ const ellipsis = '\u2026';
+ const shortenedPaths: string[] = new Array(paths.length);
+
+ // for every path
+ let match = false;
+ for (let pathIndex = 0; pathIndex < paths.length; pathIndex++) {
+ const segments: string[] = paths[pathIndex].split(nativeSep);
+ match = true;
+
+ // pick the first shortest subpath found
+ for (let subpathLength = 1; match && subpathLength <= segments.length; subpathLength++) {
+ for (let start = segments.length - subpathLength; match && start >= 0; start--) {
+ match = false;
+ const subpath = segments.slice(start, start + subpathLength).join(nativeSep);
+
+ // that is unique to any other path
+ for (let otherPathIndex = 0; !match && otherPathIndex < paths.length; otherPathIndex++) {
+
+ // suffix subpath treated specially as we consider no match 'x' and 'x/...'
+ if (otherPathIndex !== pathIndex && paths[otherPathIndex].indexOf(subpath) > -1) {
+ const isSubpathEnding: boolean = (start + subpathLength === segments.length);
+ const isOtherPathEnding: boolean = endsWith(paths[otherPathIndex], subpath);
+
+ match = !isSubpathEnding || isOtherPathEnding;
+ }
+ }
+
+ // found unique subpath
+ if (!match) {
+ let result = subpath;
+ if (start + subpathLength < segments.length) {
+ result = result + nativeSep + ellipsis;
+ }
+
+ if (start > 0) {
+ result = ellipsis + nativeSep + result;
+ }
+
+ shortenedPaths[pathIndex] = result;
+ }
+ }
+ }
+
+ if (match) {
+ shortenedPaths[pathIndex] = paths[pathIndex]; // use full path if no unique subpaths found
+ }
+ }
+
+ return shortenedPaths;
}
\ No newline at end of file
diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts
index 352ae28a8ec..11e39ca9fc1 100644
--- a/src/vs/base/common/paths.ts
+++ b/src/vs/base/common/paths.ts
@@ -393,4 +393,4 @@ export const isAbsoluteRegex = /^((\/|[a-zA-Z]:\\)[^\(\)<>\\'\"\[\]]+)/;
*/
export function isAbsolute(path: string): boolean {
return isAbsoluteRegex.test(path);
-}
+}
\ No newline at end of file
diff --git a/src/vs/base/test/browser/browser.test.ts b/src/vs/base/test/browser/browser.test.ts
index 6992e882bf4..e0ce8cd3114 100644
--- a/src/vs/base/test/browser/browser.test.ts
+++ b/src/vs/base/test/browser/browser.test.ts
@@ -6,43 +6,9 @@
import * as assert from 'assert';
import { isWindows, isMacintosh } from 'vs/base/common/platform';
-import * as browser from 'vs/base/browser/browser';
suite('Browsers', () => {
test('all', function () {
assert(!(isWindows && isMacintosh));
-
- let isOpera = browser.isOpera || navigator.userAgent.indexOf('OPR') >= 0;
- let isIE = browser.isIE;
- let isFirefox = browser.isFirefox;
- let isWebKit = browser.isWebKit;
- let isChrome = browser.isChrome;
- let isSafari = browser.isSafari;
-
- let hasCSSAnimations = browser.hasCSSAnimationSupport();
-
- let browserCount = 0;
- if (isOpera) {
- browserCount++;
- }
- if (isIE) {
- browserCount++;
- }
- if (isFirefox) {
- browserCount++;
- assert(hasCSSAnimations);
- }
- if (isWebKit) {
- browserCount++;
- assert(hasCSSAnimations);
- }
- if (isChrome) {
- browserCount++;
- assert(hasCSSAnimations);
- }
- if (isSafari) {
- browserCount++;
- assert(hasCSSAnimations);
- }
});
});
diff --git a/src/vs/base/test/browser/builder.test.ts b/src/vs/base/test/browser/builder.test.ts
index 6e122e08f4b..99c3f4ec4ee 100644
--- a/src/vs/base/test/browser/builder.test.ts
+++ b/src/vs/base/test/browser/builder.test.ts
@@ -792,14 +792,14 @@ suite('Builder', () => {
b.div();
b.show();
- assert(!b.hasClass('hidden'));
+ assert(!b.hasClass('builder-hidden'));
assert(!b.isHidden());
b.toggleVisibility();
assert(!b.isHidden());
assert(b.hasClass('builder-visible'));
b.toggleVisibility();
b.hide();
- assert(b.hasClass('hidden'));
+ assert(b.hasClass('builder-hidden'));
assert(b.isHidden());
});
@@ -808,10 +808,10 @@ suite('Builder', () => {
b.div().hide();
b.showDelayed(20);
- assert(b.hasClass('hidden'));
+ assert(b.hasClass('builder-hidden'));
TPromise.timeout(30).then(() => {
- assert(!b.hasClass('hidden'));
+ assert(!b.hasClass('builder-hidden'));
done();
});
});
@@ -821,12 +821,12 @@ suite('Builder', () => {
b.div().hide();
b.showDelayed(20);
- assert(b.hasClass('hidden'));
+ assert(b.hasClass('builder-hidden'));
b.hide(); // Should cancel the visibility promise
TPromise.timeout(30).then(() => {
- assert(b.hasClass('hidden'));
+ assert(b.hasClass('builder-hidden'));
done();
});
});
diff --git a/src/vs/base/test/common/comparers.test.ts b/src/vs/base/test/common/comparers.test.ts
index b1b475303e0..2f7b645a405 100644
--- a/src/vs/base/test/common/comparers.test.ts
+++ b/src/vs/base/test/common/comparers.test.ts
@@ -12,10 +12,16 @@ suite('Comparers', () => {
test('compareFileNames', () => {
+ assert(compareFileNames(null, null) === 0, 'null should be equal');
+ assert(compareFileNames(null, 'abc') < 0, 'null should be come before real values');
assert(compareFileNames('', '') === 0, 'empty should be equal');
assert(compareFileNames('abc', 'abc') === 0, 'equal names should be equal');
assert(compareFileNames('.abc', '.abc') === 0, 'equal full names should be equal');
- assert(compareFileNames('.env', '.env.example') < 0);
- assert(compareFileNames('.env.example', '.gitattributes') < 0);
+ assert(compareFileNames('.env', '.env.example') < 0, 'filenames with extensions should come after those without');
+ assert(compareFileNames('.env.example', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly');
+ assert(compareFileNames('1', '1') === 0, 'numerically equal full names should be equal');
+ assert(compareFileNames('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
+ assert(compareFileNames('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
+ assert(compareFileNames('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long');
});
});
diff --git a/src/vs/base/test/common/labels.test.ts b/src/vs/base/test/common/labels.test.ts
new file mode 100644
index 00000000000..ad3a39beb78
--- /dev/null
+++ b/src/vs/base/test/common/labels.test.ts
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 labels = require('vs/base/common/labels');
+
+suite('Labels', () => {
+ test('shorten', () => {
+ assert.ok(true);
+
+ // nothing to shorten
+ // assert.deepEqual(labels.shorten(['a']), ['a']);
+ // assert.deepEqual(labels.shorten(['a', 'b']), ['a', 'b']);
+ // assert.deepEqual(labels.shorten(['a', 'b', 'c']), ['a', 'b', 'c']);
+
+ // // completely different paths
+ // assert.deepEqual(labels.shorten(['a\\b', 'c\\d', 'e\\f']), ['…\\b', '…\\d', '…\\f']);
+
+ // // same beginning
+ // assert.deepEqual(labels.shorten(['a', 'a\\b']), ['a', '…\\b']);
+ // assert.deepEqual(labels.shorten(['a\\b', 'a\\b\\c']), ['…\\b', '…\\c']);
+ // assert.deepEqual(labels.shorten(['a', 'a\\b', 'a\\b\\c']), ['a', '…\\b', '…\\c']);
+ // assert.deepEqual(labels.shorten(['x:\\a\\b', 'x:\\a\\c']), ['…\\b', '…\\c'], 'TODO: drive letter (or schema) should be preserved');
+ // assert.deepEqual(labels.shorten(['\\\\a\\b', '\\\\a\\c']), ['…\\b', '…\\c'], 'TODO: root uri should be preserved');
+
+ // // same ending
+ // assert.deepEqual(labels.shorten(['a', 'b\\a']), ['a', 'b\\…']);
+ // assert.deepEqual(labels.shorten(['a\\b\\c', 'd\\b\\c']), ['a\\…', 'd\\…']);
+ // assert.deepEqual(labels.shorten(['a\\b\\c\\d', 'f\\b\\c\\d']), ['a\\…', 'f\\…']);
+ // assert.deepEqual(labels.shorten(['d\\e\\a\\b\\c', 'd\\b\\c']), ['…\\a\\…', 'd\\b\\…']);
+ // assert.deepEqual(labels.shorten(['a\\b\\c\\d', 'a\\f\\b\\c\\d']), ['a\\b\\…', '…\\f\\…']);
+ // assert.deepEqual(labels.shorten(['a\\b\\a', 'b\\b\\a']), ['a\\b\\…', 'b\\b\\…']);
+ // assert.deepEqual(labels.shorten(['d\\f\\a\\b\\c', 'h\\d\\b\\c']), ['…\\a\\…', 'h\\…']);
+ // assert.deepEqual(labels.shorten(['a\\b\\c', 'x:\\0\\a\\b\\c']), ['a\\b\\c', '…\\0\\…'], 'TODO: drive letter (or schema) should be always preserved');
+ // assert.deepEqual(labels.shorten(['x:\\a\\b', 'y:\\a\\b']), ['x:\\…', 'y:\\…']);
+ // assert.deepEqual(labels.shorten(['\\\\x\\b', '\\\\y\\b']), ['…\\x\\…', '…\\y\\…'], 'TODO: \\\\x instead of …\\x');
+
+ // // same in the middle
+ // assert.deepEqual(labels.shorten(['a\\b\\c', 'd\\b\\e']), ['…\\c', '…\\e']);
+
+ // // case-sensetive
+ // assert.deepEqual(labels.shorten(['a\\b\\c', 'd\\b\\C']), ['…\\c', '…\\C']);
+
+ // assert.deepEqual(labels.shorten(['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']), ['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']);
+ // assert.deepEqual(labels.shorten(['a', 'a\\b', 'b']), ['a', 'a\\b', 'b']);
+ // assert.deepEqual(labels.shorten(['', 'a', 'b', 'b\\c', 'a\\c']), ['', 'a', 'b', 'b\\c', 'a\\c']);
+ // assert.deepEqual(labels.shorten(['src\\vs\\workbench\\parts\\execution\\electron-browser', 'src\\vs\\workbench\\parts\\execution\\electron-browser\\something', 'src\\vs\\workbench\\parts\\terminal\\electron-browser']), ['…\\execution\\electron-browser', '…\\something', '…\\terminal\\…']);
+ });
+});
\ No newline at end of file
diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts
index ef6a310c847..de09965d895 100644
--- a/src/vs/code/electron-main/main.ts
+++ b/src/vs/code/electron-main/main.ts
@@ -48,7 +48,8 @@ import { RequestService } from 'vs/platform/request/node/requestService';
import { IURLService } from 'vs/platform/url/common/url';
import { URLChannel } from 'vs/platform/url/common/urlIpc';
import { URLService } from 'vs/platform/url/electron-main/urlService';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts
index 612ff3f0393..edb452cbc23 100644
--- a/src/vs/code/node/cliProcessMain.ts
+++ b/src/vs/code/node/cliProcessMain.ts
@@ -20,7 +20,8 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ
import { IExtensionManagementService, IExtensionGalleryService, IExtensionManifest, IGalleryExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
-import { ITelemetryService, combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
import { IRequestService } from 'vs/platform/request/node/request';
diff --git a/src/vs/code/node/sharedProcessMain.ts b/src/vs/code/node/sharedProcessMain.ts
index a8a2ecd6dc6..d6ca3fa783c 100644
--- a/src/vs/code/node/sharedProcessMain.ts
+++ b/src/vs/code/node/sharedProcessMain.ts
@@ -22,7 +22,8 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { IRequestService } from 'vs/platform/request/node/request';
import { RequestService } from 'vs/platform/request/node/requestService';
-import { ITelemetryService, combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
diff --git a/src/vs/editor/browser/controller/keyboardHandler.ts b/src/vs/editor/browser/controller/keyboardHandler.ts
index 070e69cb887..89ceea1c90a 100644
--- a/src/vs/editor/browser/controller/keyboardHandler.ts
+++ b/src/vs/editor/browser/controller/keyboardHandler.ts
@@ -157,7 +157,7 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
}
public focusTextArea(): void {
- this.textAreaHandler.writePlaceholderAndSelectTextAreaSync();
+ this.textAreaHandler.focusTextArea();
}
public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean {
diff --git a/src/vs/editor/browser/editor.all.ts b/src/vs/editor/browser/editor.all.ts
index 18a65b277a0..c8a31237358 100644
--- a/src/vs/editor/browser/editor.all.ts
+++ b/src/vs/editor/browser/editor.all.ts
@@ -35,7 +35,6 @@ import 'vs/editor/contrib/rename/browser/rename';
import 'vs/editor/contrib/smartSelect/common/smartSelect';
import 'vs/editor/contrib/snippet/common/snippet';
import 'vs/editor/contrib/snippet/browser/snippet';
-import 'vs/editor/contrib/suggest/common/snippetCompletion';
import 'vs/editor/contrib/suggest/browser/suggestController';
import 'vs/editor/contrib/suggest/browser/tabCompletion';
import 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode';
diff --git a/src/vs/editor/browser/standalone/colorizer.ts b/src/vs/editor/browser/standalone/colorizer.ts
index a185df8f084..cc370e16139 100644
--- a/src/vs/editor/browser/standalone/colorizer.ts
+++ b/src/vs/editor/browser/standalone/colorizer.ts
@@ -9,9 +9,8 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IModel } from 'vs/editor/common/editorCommon';
import { TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes';
import { IModeService } from 'vs/editor/common/services/modeService';
-import { renderLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer';
+import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
-import { LineParts } from 'vs/editor/common/core/lineParts';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import * as strings from 'vs/base/common/strings';
import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService';
@@ -96,14 +95,16 @@ export class Colorizer {
}
public static colorizeLine(line: string, tokens: ViewLineToken[], tabSize: number = 4): string {
- let renderResult = renderLine(new RenderLineInput(
+ let renderResult = renderViewLine(new RenderLineInput(
line,
+ 0,
+ tokens,
+ [],
tabSize,
0,
-1,
'none',
- false,
- new LineParts(tokens, line.length + 1)
+ false
));
return renderResult.output;
}
@@ -126,14 +127,16 @@ function _fakeColorize(lines: string[], tabSize: number): string {
for (let i = 0, length = lines.length; i < length; i++) {
let line = lines[i];
- let renderResult = renderLine(new RenderLineInput(
+ let renderResult = renderViewLine(new RenderLineInput(
line,
+ 0,
+ [new ViewLineToken(line.length, '')],
+ [],
tabSize,
0,
-1,
'none',
- false,
- new LineParts([], line.length + 1)
+ false
));
html = html.concat(renderResult.output);
@@ -152,14 +155,16 @@ function _actualColorize(lines: string[], tabSize: number, tokenizationSupport:
let line = lines[i];
let tokenizeResult = tokenizationSupport.tokenize2(line, state, 0);
let lineTokens = new LineTokens(colorMap, tokenizeResult.tokens, line);
- let renderResult = renderLine(new RenderLineInput(
+ let renderResult = renderViewLine(new RenderLineInput(
line,
+ 0,
+ lineTokens.inflate(),
+ [],
tabSize,
0,
-1,
'none',
- false,
- new LineParts(lineTokens.inflate(), line.length + 1)
+ false
));
html = html.concat(renderResult.output);
diff --git a/src/vs/editor/browser/standalone/simpleServices.ts b/src/vs/editor/browser/standalone/simpleServices.ts
index 7215702e5a6..2e43cc59605 100644
--- a/src/vs/editor/browser/standalone/simpleServices.ts
+++ b/src/vs/editor/browser/standalone/simpleServices.ts
@@ -10,9 +10,7 @@ import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, getConfigurationValue, IConfigurationKeys } from 'vs/platform/configuration/common/configuration';
import { IEditor, IEditorInput, IEditorOptions, IEditorService, IResourceInput, Position } from 'vs/platform/editor/common/editor';
-import { AbstractExtensionService, ActivatedExtension } from 'vs/platform/extensions/common/abstractExtensionService';
-import { IExtensionDescription, IExtensionService } from 'vs/platform/extensions/common/extensions';
-import { ICommandService, ICommand, ICommandHandler } from 'vs/platform/commands/common/commands';
+import { ICommandService, ICommand, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService';
import { KeybindingResolver, IOSupport } from 'vs/platform/keybinding/common/keybindingResolver';
import { IKeybindingEvent, IKeybindingItem, KeybindingSource } from 'vs/platform/keybinding/common/keybinding';
@@ -23,7 +21,6 @@ import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { Selection } from 'vs/editor/common/core/selection';
import Event, { Emitter } from 'vs/base/common/event';
import { getDefaultValues as getDefaultConfiguration } from 'vs/platform/configuration/common/model';
-import { CommandService } from 'vs/platform/commands/common/commandService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress';
import { ITextModelResolverService, ITextModelContentProvider, ITextEditorModel } from 'vs/editor/common/services/resolverService';
@@ -31,6 +28,10 @@ import { IDisposable, IReference, ImmortalReference } from 'vs/base/common/lifec
import * as dom from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
+import { values } from 'vs/base/common/collections';
+import { MenuId, MenuRegistry, ICommandAction, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
+import { Menu } from 'vs/platform/actions/common/menu';
+import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
export class SimpleEditor implements IEditor {
@@ -263,16 +264,14 @@ export class SimpleMessageService implements IMessageService {
}
}
-export class StandaloneCommandService extends CommandService {
+export class StandaloneCommandService implements ICommandService {
+ _serviceBrand: any;
+ private readonly _instantiationService: IInstantiationService;
private _dynamicCommands: { [id: string]: ICommand; };
- constructor(
- instantiationService: IInstantiationService,
- extensionService: IExtensionService
- ) {
- super(instantiationService, extensionService);
-
+ constructor(instantiationService: IInstantiationService) {
+ this._instantiationService = instantiationService;
this._dynamicCommands = Object.create(null);
}
@@ -280,8 +279,18 @@ export class StandaloneCommandService extends CommandService {
this._dynamicCommands[id] = command;
}
- protected _getCommand(id: string): ICommand {
- return super._getCommand(id) || this._dynamicCommands[id];
+ public executeCommand(id: string, ...args: any[]): TPromise {
+ const command = (CommandsRegistry.getCommand(id) || this._dynamicCommands[id]);
+ if (!command) {
+ return TPromise.wrapError(new Error(`command '${id}' not found`));
+ }
+
+ try {
+ const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler].concat(args));
+ return TPromise.as(result);
+ } catch (err) {
+ return TPromise.wrapError(err);
+ }
}
}
@@ -353,38 +362,6 @@ export class StandaloneKeybindingService extends AbstractKeybindingService {
}
}
-export class SimpleExtensionService extends AbstractExtensionService {
-
- constructor() {
- super(true);
- }
-
- protected _showMessage(severity: Severity, msg: string): void {
- switch (severity) {
- case Severity.Error:
- console.error(msg);
- break;
- case Severity.Warning:
- console.warn(msg);
- break;
- case Severity.Info:
- console.info(msg);
- break;
- default:
- console.log(msg);
- }
- }
-
- protected _createFailedExtension(): ActivatedExtension {
- throw new Error('unexpected');
- }
-
- protected _actualActivateExtension(extensionDescription: IExtensionDescription): TPromise {
- throw new Error('unexpected');
- }
-
-}
-
export class SimpleConfigurationService implements IConfigurationService {
_serviceBrand: any;
@@ -418,3 +395,40 @@ export class SimpleConfigurationService implements IConfigurationService {
return { default: [], user: [] };
}
}
+
+export class SimpleMenuService implements IMenuService {
+
+ _serviceBrand: any;
+
+ private readonly _commandService: ICommandService;
+
+ constructor(commandService: ICommandService) {
+ this._commandService = commandService;
+ }
+
+ public createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu {
+ return new Menu(id, TPromise.as(true), this._commandService, contextKeyService);
+ }
+
+ public getCommandActions(): ICommandAction[] {
+ return values(MenuRegistry.commands);
+ }
+}
+
+export class StandaloneTelemetryService implements ITelemetryService {
+ _serviceBrand: void;
+
+ public isOptedIn = false;
+
+ public publicLog(eventName: string, data?: any): TPromise {
+ return TPromise.as(null);
+ }
+
+ public getTelemetryInfo(): TPromise {
+ return null;
+ }
+
+ public getExperiments(): ITelemetryExperiments {
+ return null;
+ }
+}
diff --git a/src/vs/editor/browser/standalone/standaloneLanguages.ts b/src/vs/editor/browser/standalone/standaloneLanguages.ts
index 14750f8b094..985eb430033 100644
--- a/src/vs/editor/browser/standalone/standaloneLanguages.ts
+++ b/src/vs/editor/browser/standalone/standaloneLanguages.ts
@@ -7,7 +7,6 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle';
-import { onWillActivate } from 'vs/platform/extensions/common/extensionsRegistry';
import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
import { IMonarchLanguage } from 'vs/editor/common/modes/monarch/monarchTypes';
import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService';
@@ -47,9 +46,8 @@ export function getLanguages(): ILanguageExtensionPoint[] {
* @event
*/
export function onLanguage(languageId: string, callback: () => void): IDisposable {
- const desired = 'onLanguage:' + languageId;
- let disposable = onWillActivate.event((activationEvent) => {
- if (activationEvent === desired) {
+ let disposable = StaticServices.modeService.get().onDidCreateMode((mode) => {
+ if (mode.getId() === languageId) {
// stop listening
disposable.dispose();
// invoke actual listener
diff --git a/src/vs/editor/browser/standalone/standaloneServices.ts b/src/vs/editor/browser/standalone/standaloneServices.ts
index c2430823fce..fbe6b0ea7ba 100644
--- a/src/vs/editor/browser/standalone/standaloneServices.ts
+++ b/src/vs/editor/browser/standalone/standaloneServices.ts
@@ -10,7 +10,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
-import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { createDecorator, IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@@ -22,23 +21,23 @@ import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IMessageService } from 'vs/platform/message/common/message';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { IStorageService, NullStorageService } from 'vs/platform/storage/common/storage';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl';
import { IModeService } from 'vs/editor/common/services/modeService';
-import { MainThreadModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
+import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl';
import {
- SimpleConfigurationService, SimpleMessageService, SimpleExtensionService,
- StandaloneKeybindingService, StandaloneCommandService, SimpleProgressService
+ SimpleConfigurationService, SimpleMenuService, SimpleMessageService,
+ SimpleProgressService, StandaloneCommandService, StandaloneKeybindingService,
+ StandaloneTelemetryService
} from 'vs/editor/browser/standalone/simpleServices';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IMenuService } from 'vs/platform/actions/common/actions';
-import { MenuService } from 'vs/platform/actions/common/menuService';
import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService';
import { StandaloneColorServiceImpl } from 'vs/editor/browser/services/standaloneColorServiceImpl';
@@ -119,19 +118,17 @@ export module StaticServices {
resource: URI.from({ scheme: 'inmemory', authority: 'model', path: '/' })
}));
- export const telemetryService = define(ITelemetryService, () => NullTelemetryService);
+ export const telemetryService = define(ITelemetryService, () => new StandaloneTelemetryService());
export const configurationService = define(IConfigurationService, () => new SimpleConfigurationService());
export const messageService = define(IMessageService, () => new SimpleMessageService());
- export const extensionService = define(IExtensionService, () => new SimpleExtensionService());
-
export const markerService = define(IMarkerService, () => new MarkerService());
- export const modeService = define(IModeService, (o) => new MainThreadModeServiceImpl(instantiationService.get(o), extensionService.get(o), configurationService.get(o)));
+ export const modeService = define(IModeService, (o) => new ModeServiceImpl());
- export const modelService = define(IModelService, (o) => new ModelServiceImpl(markerService.get(o), configurationService.get(o), messageService.get(o)));
+ export const modelService = define(IModelService, (o) => new ModelServiceImpl(markerService.get(o), configurationService.get(o)));
export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), configurationService.get(o)));
@@ -157,7 +154,6 @@ export class DynamicStandaloneServices extends Disposable {
this._instantiationService = _instantiationService;
const configurationService = this.get(IConfigurationService);
- const extensionService = this.get(IExtensionService);
const messageService = this.get(IMessageService);
const telemetryService = this.get(ITelemetryService);
@@ -175,7 +171,7 @@ export class DynamicStandaloneServices extends Disposable {
let contextKeyService = ensure(IContextKeyService, () => this._register(new ContextKeyService(configurationService)));
- let commandService = ensure(ICommandService, () => new StandaloneCommandService(this._instantiationService, extensionService));
+ let commandService = ensure(ICommandService, () => new StandaloneCommandService(this._instantiationService));
ensure(IKeybindingService, () => this._register(new StandaloneKeybindingService(contextKeyService, commandService, messageService, domElement)));
@@ -183,7 +179,7 @@ export class DynamicStandaloneServices extends Disposable {
ensure(IContextMenuService, () => this._register(new ContextMenuService(domElement, telemetryService, messageService, contextViewService)));
- ensure(IMenuService, () => new MenuService(extensionService, commandService));
+ ensure(IMenuService, () => new SimpleMenuService(commandService));
}
public get(serviceId: ServiceIdentifier): T {
diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts
index 2766ef2f051..cd57262869d 100644
--- a/src/vs/editor/browser/view/viewImpl.ts
+++ b/src/vs/editor/browser/view/viewImpl.ts
@@ -673,7 +673,9 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
this.keyboardHandler.focusTextArea();
// IE does not trigger the focus event immediately, so we must help it a little bit
- this._setHasFocus(true);
+ if (document.activeElement === this.textArea) {
+ this._setHasFocus(true);
+ }
}
public isFocused(): boolean {
diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts
index 7f278afa2f0..14bebc00d2a 100644
--- a/src/vs/editor/browser/viewParts/lines/viewLine.ts
+++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts
@@ -7,8 +7,8 @@
import * as browser from 'vs/base/browser/browser';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/styleMutator';
import { IConfigurationChangedEvent } from 'vs/editor/common/editorCommon';
-import { createLineParts } from 'vs/editor/common/viewLayout/viewLineParts';
-import { renderLine, RenderLineInput, RenderLineOutput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer';
+import { Decoration } from 'vs/editor/common/viewLayout/viewLineParts';
+import { renderViewLine, RenderLineInput, RenderLineOutput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer';
import { ClassNames } from 'vs/editor/browser/editorBrowser';
import { IVisibleLineData } from 'vs/editor/browser/view/viewLayer';
import { RangeUtil } from 'vs/editor/browser/viewParts/lines/rangeUtil';
@@ -97,24 +97,19 @@ export class ViewLine implements IVisibleLineData {
}
this._isMaybeInvalid = false;
- let newLineParts = createLineParts(
- lineNumber,
- this._context.model.getLineMinColumn(lineNumber),
- this._context.model.getLineContent(lineNumber),
- this._context.model.getTabSize(),
- this._context.model.getLineTokens(lineNumber),
- inlineDecorations,
- this._renderWhitespace
- );
+ const model = this._context.model;
+ const actualInlineDecorations = Decoration.filter(inlineDecorations, lineNumber, model.getLineMinColumn(lineNumber), model.getLineMaxColumn(lineNumber));
let renderLineInput = new RenderLineInput(
- this._context.model.getLineContent(lineNumber),
- this._context.model.getTabSize(),
+ model.getLineContent(lineNumber),
+ model.getLineMinColumn(lineNumber) - 1,
+ model.getLineTokens(lineNumber),
+ actualInlineDecorations,
+ model.getTabSize(),
this._spaceWidth,
this._stopRenderingLineAfter,
this._renderWhitespace,
- this._renderControlCharacters,
- newLineParts
+ this._renderControlCharacters
);
if (this._renderedViewLine && this._renderedViewLine.input.equals(renderLineInput)) {
@@ -122,11 +117,14 @@ export class ViewLine implements IVisibleLineData {
return false;
}
+ let isWhitespaceOnly = /^\s*$/.test(renderLineInput.lineContent);
+
this._renderedViewLine = createRenderedLine(
this._renderedViewLine ? this._renderedViewLine.domNode : null,
renderLineInput,
this._context.model.mightContainRTL(),
- renderLine(renderLineInput)
+ isWhitespaceOnly,
+ renderViewLine(renderLineInput)
);
return true;
}
@@ -183,18 +181,18 @@ class RenderedViewLine {
*/
private _pixelOffsetCache: number[];
- constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, modelContainsRTL: boolean, renderLineOutput: RenderLineOutput) {
+ constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, modelContainsRTL: boolean, isWhitespaceOnly: boolean, renderLineOutput: RenderLineOutput) {
this.domNode = domNode;
this.input = renderLineInput;
this.html = renderLineOutput.output;
this._characterMapping = renderLineOutput.characterMapping;
- this._isWhitespaceOnly = renderLineOutput.isWhitespaceOnly;
+ this._isWhitespaceOnly = isWhitespaceOnly;
this._cachedWidth = -1;
this._pixelOffsetCache = null;
if (!modelContainsRTL) {
this._pixelOffsetCache = [];
- for (let column = 0, maxLineColumn = this.input.lineParts.maxLineColumn; column <= maxLineColumn; column++) {
+ for (let column = 0, len = this._characterMapping.length; column <= len; column++) {
this._pixelOffsetCache[column] = -1;
}
}
@@ -376,17 +374,17 @@ class WebKitRenderedViewLine extends RenderedViewLine {
}
}
-const createRenderedLine: (domNode: FastDomNode, renderLineInput: RenderLineInput, modelContainsRTL: boolean, renderLineOutput: RenderLineOutput) => RenderedViewLine = (function () {
+const createRenderedLine: (domNode: FastDomNode, renderLineInput: RenderLineInput, modelContainsRTL: boolean, isWhitespaceOnly: boolean, renderLineOutput: RenderLineOutput) => RenderedViewLine = (function () {
if (browser.isWebKit) {
return createWebKitRenderedLine;
}
return createNormalRenderedLine;
})();
-function createWebKitRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, modelContainsRTL: boolean, renderLineOutput: RenderLineOutput): RenderedViewLine {
- return new WebKitRenderedViewLine(domNode, renderLineInput, modelContainsRTL, renderLineOutput);
+function createWebKitRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, modelContainsRTL: boolean, isWhitespaceOnly: boolean, renderLineOutput: RenderLineOutput): RenderedViewLine {
+ return new WebKitRenderedViewLine(domNode, renderLineInput, modelContainsRTL, isWhitespaceOnly, renderLineOutput);
}
-function createNormalRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, modelContainsRTL: boolean, renderLineOutput: RenderLineOutput): RenderedViewLine {
- return new RenderedViewLine(domNode, renderLineInput, modelContainsRTL, renderLineOutput);
+function createNormalRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, modelContainsRTL: boolean, isWhitespaceOnly: boolean, renderLineOutput: RenderLineOutput): RenderedViewLine {
+ return new RenderedViewLine(domNode, renderLineInput, modelContainsRTL, isWhitespaceOnly, renderLineOutput);
}
diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts
index ea6e7b708a4..8a423b577ab 100644
--- a/src/vs/editor/browser/widget/diffEditorWidget.ts
+++ b/src/vs/editor/browser/widget/diffEditorWidget.ts
@@ -19,11 +19,11 @@ import { DefaultConfig } from 'vs/editor/common/config/defaultConfig';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
-import { createLineParts } from 'vs/editor/common/viewLayout/viewLineParts';
-import { renderLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer';
+import { Decoration } from 'vs/editor/common/viewLayout/viewLineParts';
+import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
-import { ViewLineToken, ViewLineTokens } from 'vs/editor/common/core/viewLineToken';
+import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
@@ -1885,17 +1885,18 @@ class InlineViewZonesComputer extends ViewZonesComputer {
private renderOriginalLine(count: number, originalModel: editorCommon.IModel, config: editorCommon.InternalEditorOptions, tabSize: number, lineNumber: number, decorations: InlineDecoration[]): string[] {
let lineContent = originalModel.getLineContent(lineNumber);
- let lineTokens = new ViewLineTokens([new ViewLineToken(0, '')], 0, lineContent.length);
- let parts = createLineParts(lineNumber, 1, lineContent, tabSize, lineTokens, decorations, config.viewInfo.renderWhitespace);
+ let actualDecorations = Decoration.filter(decorations, lineNumber, 1, lineContent.length + 1);
- let r = renderLine(new RenderLineInput(
+ let r = renderViewLine(new RenderLineInput(
lineContent,
+ 0,
+ [new ViewLineToken(lineContent.length, '')],
+ actualDecorations,
tabSize,
config.fontInfo.spaceWidth,
config.viewInfo.stopRenderingLineAfter,
config.viewInfo.renderWhitespace,
- config.viewInfo.renderControlCharacters,
- parts
+ config.viewInfo.renderControlCharacters
));
let myResult: string[] = [];
diff --git a/src/vs/editor/common/controller/textAreaHandler.ts b/src/vs/editor/common/controller/textAreaHandler.ts
index 1015da7d3fb..a385e1c96a8 100644
--- a/src/vs/editor/common/controller/textAreaHandler.ts
+++ b/src/vs/editor/common/controller/textAreaHandler.ts
@@ -118,7 +118,7 @@ export class TextAreaHandler extends Disposable {
// In IE we cannot set .value when handling 'compositionstart' because the entire composition will get canceled.
if (!this.Browser.isEdgeOrIE) {
- this.setTextAreaState('compositionstart', this.textAreaState.toEmpty());
+ this.setTextAreaState('compositionstart', this.textAreaState.toEmpty(), false);
}
this._onCompositionStart.fire({
@@ -200,13 +200,13 @@ export class TextAreaHandler extends Disposable {
} else {
if (this.textArea.getSelectionStart() !== this.textArea.getSelectionEnd()) {
// Clean up the textarea, to get a clean paste
- this.setTextAreaState('paste', this.textAreaState.toEmpty());
+ this.setTextAreaState('paste', this.textAreaState.toEmpty(), false);
}
this._nextCommand = ReadFromTextArea.Paste;
}
}));
- this._writePlaceholderAndSelectTextArea('ctor');
+ this._writePlaceholderAndSelectTextArea('ctor', false);
}
public dispose(): void {
@@ -227,24 +227,24 @@ export class TextAreaHandler extends Disposable {
}
this.hasFocus = isFocused;
if (this.hasFocus) {
- this._writePlaceholderAndSelectTextArea('focusgain');
+ this._writePlaceholderAndSelectTextArea('focusgain', false);
}
}
public setCursorSelections(primary: Range, secondary: Range[]): void {
this.selection = primary;
this.selections = [primary].concat(secondary);
- this._writePlaceholderAndSelectTextArea('selection changed');
+ this._writePlaceholderAndSelectTextArea('selection changed', false);
}
// --- end event handlers
- private setTextAreaState(reason: string, textAreaState: TextAreaState): void {
+ private setTextAreaState(reason: string, textAreaState: TextAreaState, forceFocus: boolean): void {
if (!this.hasFocus) {
textAreaState = textAreaState.resetSelection();
}
- textAreaState.applyToTextArea(reason, this.textArea, this.hasFocus);
+ textAreaState.applyToTextArea(reason, this.textArea, this.hasFocus || forceFocus);
this.textAreaState = textAreaState;
}
@@ -281,18 +281,18 @@ export class TextAreaHandler extends Disposable {
});
}
- public writePlaceholderAndSelectTextAreaSync(): void {
- this._writePlaceholderAndSelectTextArea('focusTextArea');
+ public focusTextArea(): void {
+ this._writePlaceholderAndSelectTextArea('focusTextArea', true);
}
- private _writePlaceholderAndSelectTextArea(reason: string): void {
+ private _writePlaceholderAndSelectTextArea(reason: string, forceFocus: boolean): void {
if (!this.textareaIsShownAtCursor) {
// Do not write to the textarea if it is visible.
if (this.Browser.isIPad) {
// Do not place anything in the textarea for the iPad
- this.setTextAreaState(reason, this.textAreaState.toEmpty());
+ this.setTextAreaState(reason, this.textAreaState.toEmpty(), forceFocus);
} else {
- this.setTextAreaState(reason, this.textAreaState.fromEditorSelection(this.model, this.selection));
+ this.setTextAreaState(reason, this.textAreaState.fromEditorSelection(this.model, this.selection), forceFocus);
}
}
}
@@ -304,7 +304,7 @@ export class TextAreaHandler extends Disposable {
if (e.canUseTextData()) {
e.setTextData(whatToCopy);
} else {
- this.setTextAreaState('copy or cut', this.textAreaState.fromText(whatToCopy));
+ this.setTextAreaState('copy or cut', this.textAreaState.fromText(whatToCopy), false);
}
if (this.Browser.enableEmptySelectionClipboard) {
diff --git a/src/vs/editor/common/core/lineParts.ts b/src/vs/editor/common/core/lineParts.ts
deleted file mode 100644
index 784049905bc..00000000000
--- a/src/vs/editor/common/core/lineParts.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * 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 { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
-
-export class LineParts {
- _linePartsBrand: void;
-
- public readonly parts: ViewLineToken[];
- public readonly maxLineColumn: number;
-
- constructor(parts: ViewLineToken[], maxLineColumn: number) {
- this.parts = parts;
- this.maxLineColumn = maxLineColumn;
- }
-
- public equals(other: LineParts): boolean {
- return (
- this.maxLineColumn === other.maxLineColumn
- && ViewLineToken.equalsArray(this.parts, other.parts)
- );
- }
-}
diff --git a/src/vs/editor/common/core/lineTokens.ts b/src/vs/editor/common/core/lineTokens.ts
index f0030ed7cb1..b1cca283dd1 100644
--- a/src/vs/editor/common/core/lineTokens.ts
+++ b/src/vs/editor/common/core/lineTokens.ts
@@ -122,6 +122,10 @@ export class LineTokens {
return this._text;
}
+ public getLineLength(): number {
+ return this._textLength;
+ }
+
public getTokenStartOffset(tokenIndex: number): number {
return this._tokens[(tokenIndex << 1)];
}
@@ -190,10 +194,10 @@ export class LineTokens {
}
public inflate(): ViewLineToken[] {
- return TokenMetadata.inflateArr(this._tokens);
+ return TokenMetadata.inflateArr(this._tokens, this._textLength);
}
- public sliceAndInflate(startOffset: number, endOffset: number, deltaStartIndex: number): ViewLineToken[] {
- return TokenMetadata.sliceAndInflate(this._tokens, startOffset, endOffset, deltaStartIndex);
+ public sliceAndInflate(startOffset: number, endOffset: number, deltaOffset: number): ViewLineToken[] {
+ return TokenMetadata.sliceAndInflate(this._tokens, startOffset, endOffset, deltaOffset, this._textLength);
}
}
diff --git a/src/vs/editor/common/core/viewLineToken.ts b/src/vs/editor/common/core/viewLineToken.ts
index 661483207df..03643e032b4 100644
--- a/src/vs/editor/common/core/viewLineToken.ts
+++ b/src/vs/editor/common/core/viewLineToken.ts
@@ -10,66 +10,35 @@
export class ViewLineToken {
_viewLineTokenBrand: void;
- public readonly startIndex: number;
+ /**
+ * last char index of this token (not inclusive).
+ */
+ public readonly endIndex: number;
public readonly type: string;
- constructor(startIndex: number, type: string) {
- this.startIndex = startIndex | 0;// @perf
+ constructor(endIndex: number, type: string) {
+ this.endIndex = endIndex;
this.type = type;
}
- public equals(other: ViewLineToken): boolean {
+ private static _equals(a: ViewLineToken, b: ViewLineToken): boolean {
return (
- this.startIndex === other.startIndex
- && this.type === other.type
+ a.endIndex === b.endIndex
+ && a.type === b.type
);
}
- public static equalsArray(a: ViewLineToken[], b: ViewLineToken[]): boolean {
- let aLen = a.length;
- let bLen = b.length;
+ public static equalsArr(a: ViewLineToken[], b: ViewLineToken[]): boolean {
+ const aLen = a.length;
+ const bLen = b.length;
if (aLen !== bLen) {
return false;
}
for (let i = 0; i < aLen; i++) {
- if (!a[i].equals(b[i])) {
+ if (!this._equals(a[i], b[i])) {
return false;
}
}
return true;
}
}
-
-export class ViewLineTokens {
- _viewLineTokensBrand: void;
-
- private _lineTokens: ViewLineToken[];
- private _fauxIndentLength: number;
- private _textLength: number;
-
- constructor(lineTokens: ViewLineToken[], fauxIndentLength: number, textLength: number) {
- this._lineTokens = lineTokens;
- this._fauxIndentLength = fauxIndentLength | 0;
- this._textLength = textLength | 0;
- }
-
- public getTokens(): ViewLineToken[] {
- return this._lineTokens;
- }
-
- public getFauxIndentLength(): number {
- return this._fauxIndentLength;
- }
-
- public getTextLength(): number {
- return this._textLength;
- }
-
- public equals(other: ViewLineTokens): boolean {
- return (
- this._fauxIndentLength === other._fauxIndentLength
- && this._textLength === other._textLength
- && ViewLineToken.equalsArray(this._lineTokens, other._lineTokens)
- );
- }
-}
diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts
index 1bc532e7c83..f8155b33a4a 100644
--- a/src/vs/editor/common/editorCommon.ts
+++ b/src/vs/editor/common/editorCommon.ts
@@ -243,10 +243,9 @@ export interface IEditorOptions {
*/
roundedSelection?: boolean;
/**
- * Theme to be used for rendering. Consists of two parts, the UI theme and the syntax theme,
- * separated by a space.
- * The current available UI themes are: 'vs' (default), 'vs-dark', 'hc-black'
- * The syntax themes are contributed. The default is 'default-theme'
+ * Theme to be used for rendering.
+ * The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'.
+ * You can create custom themes via `monaco.editor.defineTheme`.
*/
theme?: string;
/**
@@ -1130,9 +1129,8 @@ export interface IModelDecorationOptions {
className?: string;
/**
* Message to be rendered when hovering over the glyph margin decoration.
- * @internal
*/
- glyphMarginHoverMessage?: string;
+ glyphMarginHoverMessage?: MarkedString | MarkedString[];
/**
* Array of MarkedString to render as the decoration message.
*/
diff --git a/src/vs/editor/common/model/textModelWithDecorations.ts b/src/vs/editor/common/model/textModelWithDecorations.ts
index 654172d5344..58d135c4ac7 100644
--- a/src/vs/editor/common/model/textModelWithDecorations.ts
+++ b/src/vs/editor/common/model/textModelWithDecorations.ts
@@ -834,8 +834,8 @@ export class ModelDecorationOptions implements editorCommon.IModelDecorationOpti
stickiness: editorCommon.TrackedRangeStickiness;
className: string;
- glyphMarginHoverMessage: string;
hoverMessage: MarkedString | MarkedString[];
+ glyphMarginHoverMessage: MarkedString | MarkedString[];
isWholeLine: boolean;
showInOverviewRuler: string;
overviewRuler: editorCommon.IModelDecorationOverviewRulerOptions;
@@ -849,8 +849,8 @@ export class ModelDecorationOptions implements editorCommon.IModelDecorationOpti
constructor(options: editorCommon.IModelDecorationOptions) {
this.stickiness = options.stickiness || editorCommon.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;
this.className = options.className ? cleanClassName(options.className) : strings.empty;
- this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || strings.empty;
this.hoverMessage = options.hoverMessage || [];
+ this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || strings.empty;
this.isWholeLine = options.isWholeLine || false;
this.overviewRuler = _normalizeOverviewRulerOptions(options.overviewRuler, options.showInOverviewRuler);
this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : strings.empty;
@@ -873,7 +873,6 @@ export class ModelDecorationOptions implements editorCommon.IModelDecorationOpti
return (
this.stickiness === other.stickiness
&& this.className === other.className
- && this.glyphMarginHoverMessage === other.glyphMarginHoverMessage
&& this.isWholeLine === other.isWholeLine
&& this.showInOverviewRuler === other.showInOverviewRuler
&& this.glyphMarginClassName === other.glyphMarginClassName
@@ -883,6 +882,7 @@ export class ModelDecorationOptions implements editorCommon.IModelDecorationOpti
&& this.beforeContentClassName === other.beforeContentClassName
&& this.afterContentClassName === other.afterContentClassName
&& markedStringsEquals(this.hoverMessage, other.hoverMessage)
+ && markedStringsEquals(this.glyphMarginHoverMessage, other.glyphMarginHoverMessage)
&& ModelDecorationOptions._overviewRulerEquals(this.overviewRuler, other.overviewRuler)
);
}
diff --git a/src/vs/editor/common/model/tokensBinaryEncoding.ts b/src/vs/editor/common/model/tokensBinaryEncoding.ts
index 18884f1de39..6876e6eb450 100644
--- a/src/vs/editor/common/model/tokensBinaryEncoding.ts
+++ b/src/vs/editor/common/model/tokensBinaryEncoding.ts
@@ -71,36 +71,35 @@ export class TokenMetadata {
return className;
}
- public static inflateArr(tokens: Uint32Array): ViewLineToken[] {
- let tokenCount = (tokens.length >>> 1);
+ public static inflateArr(tokens: Uint32Array, lineLength: number): ViewLineToken[] {
let result: ViewLineToken[] = [];
- for (let i = 0; i < tokenCount; i++) {
- let startOffset = tokens[(i << 1)];
+ for (let i = 0, len = (tokens.length >>> 1); i < len; i++) {
+ let endOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
let metadata = tokens[(i << 1) + 1];
- result[i] = new ViewLineToken(startOffset, this._getClassNameFromMetadata(metadata));
+ result[i] = new ViewLineToken(endOffset, this._getClassNameFromMetadata(metadata));
}
+
return result;
}
- public static sliceAndInflate(tokens: Uint32Array, startOffset: number, endOffset: number, deltaStartIndex: number): ViewLineToken[] {
+ public static sliceAndInflate(tokens: Uint32Array, startOffset: number, endOffset: number, deltaOffset: number, lineLength: number): ViewLineToken[] {
let tokenIndex = this.findIndexInSegmentsArray(tokens, startOffset);
let result: ViewLineToken[] = [], resultLen = 0;
- result[resultLen++] = new ViewLineToken(0, this._getClassNameFromMetadata(tokens[(tokenIndex << 1) + 1]));
+ for (let i = tokenIndex, len = (tokens.length >>> 1); i < len; i++) {
+ let tokenStartOffset = tokens[(i << 1)];
- for (let i = tokenIndex + 1, len = (tokens.length >>> 1); i < len; i++) {
- let originalStartOffset = tokens[(i << 1)];
-
- if (originalStartOffset >= endOffset) {
+ if (tokenStartOffset >= endOffset) {
break;
}
- let newStartOffset = originalStartOffset - startOffset + deltaStartIndex;
+ let tokenEndOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
+ let newEndOffset = tokenEndOffset - startOffset + deltaOffset;
let metadata = tokens[(i << 1) + 1];
- result[resultLen++] = new ViewLineToken(newStartOffset, this._getClassNameFromMetadata(metadata));
+ result[resultLen++] = new ViewLineToken(newEndOffset, this._getClassNameFromMetadata(metadata));
}
return result;
diff --git a/src/vs/editor/common/modes/textToHtmlTokenizer.ts b/src/vs/editor/common/modes/textToHtmlTokenizer.ts
index 19795f0681a..e81acfc9802 100644
--- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts
+++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts
@@ -41,16 +41,11 @@ function _tokenizeToString(text: string, tokenizationSupport: ITokenizationSuppo
let viewLineTokens = lineTokens.inflate();
let startOffset = 0;
- let className = viewLineTokens[0].type;
-
- for (let j = 1, lenJ = viewLineTokens.length; j < lenJ; j++) {
- let viewLineToken = viewLineTokens[j];
-
- result += `${strings.escape(line.substring(startOffset, viewLineToken.startIndex))}`;
- startOffset = viewLineToken.startIndex;
- className = viewLineToken.type;
+ for (let j = 0, lenJ = viewLineTokens.length; j < lenJ; j++) {
+ const viewLineToken = viewLineTokens[j];
+ result += `${strings.escape(line.substring(startOffset, viewLineToken.endIndex))}`;
+ startOffset = viewLineToken.endIndex;
}
- result += `${strings.escape(line.substring(startOffset))}`;
currentState = tokenizationResult.endState;
}
diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts
index 924e16c758b..93bfaad7f64 100644
--- a/src/vs/editor/common/services/modeServiceImpl.ts
+++ b/src/vs/editor/common/services/modeServiceImpl.ts
@@ -7,19 +7,12 @@
import * as nls from 'vs/nls';
import { onUnexpectedError } from 'vs/base/common/errors';
import Event, { Emitter } from 'vs/base/common/event';
-import * as paths from 'vs/base/common/paths';
import { TPromise } from 'vs/base/common/winjs.base';
-import mime = require('vs/base/common/mime');
-import { IFilesConfiguration } from 'vs/platform/files/common/files';
-import { IExtensionService } from 'vs/platform/extensions/common/extensions';
-import { IExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
-import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { IExtensionPoint, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import { FrankensteinMode } from 'vs/editor/common/modes/abstractMode';
-import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry';
-import { ILanguageExtensionPoint, IValidLanguageExtensionPoint, IModeLookupResult, IModeService } from 'vs/editor/common/services/modeService';
-import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { ILanguageExtensionPoint, IModeLookupResult, IModeService } from 'vs/editor/common/services/modeService';
export const languagesExtPoint: IExtensionPoint = ExtensionsRegistry.registerExtensionPoint('languages', [], {
description: nls.localize('vscode.extension.contributes.languages', 'Contributes language declarations.'),
@@ -81,58 +74,9 @@ export const languagesExtPoint: IExtensionPoint = Ext
}
});
-function isUndefinedOrStringArray(value: string[]): boolean {
- if (typeof value === 'undefined') {
- return true;
- }
- if (!Array.isArray(value)) {
- return false;
- }
- return value.every(item => typeof item === 'string');
-}
-
-function isValidLanguageExtensionPoint(value: ILanguageExtensionPoint, collector: ExtensionMessageCollector): boolean {
- if (!value) {
- collector.error(nls.localize('invalid.empty', "Empty value for `contributes.{0}`", languagesExtPoint.name));
- return false;
- }
- if (typeof value.id !== 'string') {
- collector.error(nls.localize('require.id', "property `{0}` is mandatory and must be of type `string`", 'id'));
- return false;
- }
- if (!isUndefinedOrStringArray(value.extensions)) {
- collector.error(nls.localize('opt.extensions', "property `{0}` can be omitted and must be of type `string[]`", 'extensions'));
- return false;
- }
- if (!isUndefinedOrStringArray(value.filenames)) {
- collector.error(nls.localize('opt.filenames', "property `{0}` can be omitted and must be of type `string[]`", 'filenames'));
- return false;
- }
- if (typeof value.firstLine !== 'undefined' && typeof value.firstLine !== 'string') {
- collector.error(nls.localize('opt.firstLine', "property `{0}` can be omitted and must be of type `string`", 'firstLine'));
- return false;
- }
- if (typeof value.configuration !== 'undefined' && typeof value.configuration !== 'string') {
- collector.error(nls.localize('opt.configuration', "property `{0}` can be omitted and must be of type `string`", 'configuration'));
- return false;
- }
- if (!isUndefinedOrStringArray(value.aliases)) {
- collector.error(nls.localize('opt.aliases', "property `{0}` can be omitted and must be of type `string[]`", 'aliases'));
- return false;
- }
- if (!isUndefinedOrStringArray(value.mimetypes)) {
- collector.error(nls.localize('opt.mimetypes', "property `{0}` can be omitted and must be of type `string[]`", 'mimetypes'));
- return false;
- }
- return true;
-}
-
export class ModeServiceImpl implements IModeService {
public _serviceBrand: any;
- private _instantiationService: IInstantiationService;
- protected _extensionService: IExtensionService;
-
private _instantiatedModes: { [modeId: string]: IMode; };
private _registry: LanguagesRegistry;
@@ -143,19 +87,17 @@ export class ModeServiceImpl implements IModeService {
private _onDidCreateMode: Emitter = new Emitter();
public onDidCreateMode: Event = this._onDidCreateMode.event;
- constructor(
- instantiationService: IInstantiationService,
- extensionService: IExtensionService
- ) {
- this._instantiationService = instantiationService;
- this._extensionService = extensionService;
-
+ constructor() {
this._instantiatedModes = {};
this._registry = new LanguagesRegistry();
this._registry.onDidAddModes((modes) => this._onDidAddModes.fire(modes));
}
+ protected _onReady(): TPromise {
+ return TPromise.as(true);
+ }
+
public isRegisteredMode(mimetypeOrModeId: string): boolean {
return this._registry.isRegisteredMode(mimetypeOrModeId);
}
@@ -188,6 +130,16 @@ export class ModeServiceImpl implements IModeService {
return this._registry.getModeIdForLanguageNameLowercase(alias);
}
+ public getModeIdByFilenameOrFirstLine(filename: string, firstLine?: string): string {
+ var modeIds = this._registry.getModeIdsFromFilenameOrFirstLine(filename, firstLine);
+
+ if (modeIds.length > 0) {
+ return modeIds[0];
+ }
+
+ return null;
+ }
+
public getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string {
var modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds);
@@ -245,7 +197,23 @@ export class ModeServiceImpl implements IModeService {
}
}
- public getModeIdByLanguageName(languageName: string): string {
+ public getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): TPromise {
+ return this._onReady().then(() => {
+ var modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds);
+ // Fall back to plain text if no mode was found
+ return this._getOrCreateMode(modeId || 'plaintext');
+ });
+ }
+
+ public getOrCreateModeByLanguageName(languageName: string): TPromise {
+ return this._onReady().then(() => {
+ var modeId = this._getModeIdByLanguageName(languageName);
+ // Fall back to plain text if no mode was found
+ return this._getOrCreateMode(modeId || 'plaintext');
+ });
+ }
+
+ private _getModeIdByLanguageName(languageName: string): string {
var modeIds = this._registry.getModeIdsFromLanguageName(languageName);
if (modeIds.length > 0) {
@@ -255,38 +223,8 @@ export class ModeServiceImpl implements IModeService {
return null;
}
- public getModeIdByFilenameOrFirstLine(filename: string, firstLine?: string): string {
- var modeIds = this._registry.getModeIdsFromFilenameOrFirstLine(filename, firstLine);
-
- if (modeIds.length > 0) {
- return modeIds[0];
- }
-
- return null;
- }
-
- public onReady(): TPromise {
- return this._extensionService.onReady();
- }
-
- public getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): TPromise {
- return this.onReady().then(() => {
- var modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds);
- // Fall back to plain text if no mode was found
- return this._getOrCreateMode(modeId || 'plaintext');
- });
- }
-
- public getOrCreateModeByLanguageName(languageName: string): TPromise {
- return this.onReady().then(() => {
- var modeId = this.getModeIdByLanguageName(languageName);
- // Fall back to plain text if no mode was found
- return this._getOrCreateMode(modeId || 'plaintext');
- });
- }
-
public getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?: string): TPromise {
- return this.onReady().then(() => {
+ return this._onReady().then(() => {
var modeId = this.getModeIdByFilenameOrFirstLine(filename, firstLine);
// Fall back to plain text if no mode was found
return this._getOrCreateMode(modeId || 'plaintext');
@@ -299,87 +237,7 @@ export class ModeServiceImpl implements IModeService {
this._instantiatedModes[modeId] = new FrankensteinMode(languageIdentifier);
this._onDidCreateMode.fire(this._instantiatedModes[modeId]);
-
- this._extensionService.activateByEvent(`onLanguage:${modeId}`).done(null, onUnexpectedError);
}
return this._instantiatedModes[modeId];
}
}
-
-export class MainThreadModeServiceImpl extends ModeServiceImpl {
- private _configurationService: IConfigurationService;
- private _onReadyPromise: TPromise;
-
- constructor(
- @IInstantiationService instantiationService: IInstantiationService,
- @IExtensionService extensionService: IExtensionService,
- @IConfigurationService configurationService: IConfigurationService
- ) {
- super(instantiationService, extensionService);
- this._configurationService = configurationService;
-
- languagesExtPoint.setHandler((extensions: IExtensionPointUser[]) => {
- let allValidLanguages: IValidLanguageExtensionPoint[] = [];
-
- for (let i = 0, len = extensions.length; i < len; i++) {
- let extension = extensions[i];
-
- if (!Array.isArray(extension.value)) {
- extension.collector.error(nls.localize('invalid', "Invalid `contributes.{0}`. Expected an array.", languagesExtPoint.name));
- continue;
- }
-
- for (let j = 0, lenJ = extension.value.length; j < lenJ; j++) {
- let ext = extension.value[j];
- if (isValidLanguageExtensionPoint(ext, extension.collector)) {
- let configuration = (ext.configuration ? paths.join(extension.description.extensionFolderPath, ext.configuration) : ext.configuration);
- allValidLanguages.push({
- id: ext.id,
- extensions: ext.extensions,
- filenames: ext.filenames,
- filenamePatterns: ext.filenamePatterns,
- firstLine: ext.firstLine,
- aliases: ext.aliases,
- mimetypes: ext.mimetypes,
- configuration: configuration
- });
- }
- }
- }
-
- ModesRegistry.registerLanguages(allValidLanguages);
-
- });
-
- this._configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(e.config));
- }
-
- public onReady(): TPromise {
- if (!this._onReadyPromise) {
- const configuration = this._configurationService.getConfiguration();
- this._onReadyPromise = this._extensionService.onReady().then(() => {
- this.onConfigurationChange(configuration);
-
- return true;
- });
- }
-
- return this._onReadyPromise;
- }
-
- private onConfigurationChange(configuration: IFilesConfiguration): void {
-
- // Clear user configured mime associations
- mime.clearTextMimes(true /* user configured */);
-
- // Register based on settings
- if (configuration.files && configuration.files.associations) {
- Object.keys(configuration.files.associations).forEach(pattern => {
- const langId = configuration.files.associations[pattern];
- const mimetype = this.getMimeForMode(langId) || `text/x-${langId}`;
-
- mime.registerTextMime({ id: langId, mime: mimetype, filepattern: pattern, userConfigured: true });
- });
- }
- }
-}
diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts
index 19223c6bdb2..1c87597e1f7 100644
--- a/src/vs/editor/common/services/modelServiceImpl.ts
+++ b/src/vs/editor/common/services/modelServiceImpl.ts
@@ -14,7 +14,6 @@ import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers';
-import { anonymize } from 'vs/platform/telemetry/common/telemetry';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
@@ -23,7 +22,6 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import * as platform from 'vs/base/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { DEFAULT_INDENTATION, DEFAULT_TRIM_AUTO_WHITESPACE } from 'vs/editor/common/config/defaultConfig';
-import { IMessageService } from 'vs/platform/message/common/message';
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
function MODEL_ID(resource: URI): string {
@@ -179,7 +177,6 @@ export class ModelServiceImpl implements IModelService {
private _markerService: IMarkerService;
private _markerServiceSubscription: IDisposable;
- private _messageService: IMessageService;
private _configurationService: IConfigurationService;
private _configurationServiceSubscription: IDisposable;
@@ -189,8 +186,6 @@ export class ModelServiceImpl implements IModelService {
private _modelCreationOptions: editorCommon.ITextModelCreationOptions;
- private _hasShownMigrationMessage: boolean;
-
/**
* All the models known in the system.
*/
@@ -199,7 +194,6 @@ export class ModelServiceImpl implements IModelService {
constructor(
@IMarkerService markerService: IMarkerService,
@IConfigurationService configurationService: IConfigurationService,
- @IMessageService messageService: IMessageService
) {
this._modelCreationOptions = {
tabSize: DEFAULT_INDENTATION.tabSize,
@@ -210,8 +204,6 @@ export class ModelServiceImpl implements IModelService {
};
this._markerService = markerService;
this._configurationService = configurationService;
- this._messageService = messageService;
- this._hasShownMigrationMessage = false;
this._models = {};
@@ -225,21 +217,17 @@ export class ModelServiceImpl implements IModelService {
let readConfig = (config: IRawConfig) => {
- let shouldShowMigrationMessage = false;
-
let tabSize = DEFAULT_INDENTATION.tabSize;
if (config.editor && typeof config.editor.tabSize !== 'undefined') {
let parsedTabSize = parseInt(config.editor.tabSize, 10);
if (!isNaN(parsedTabSize)) {
tabSize = parsedTabSize;
}
- shouldShowMigrationMessage = shouldShowMigrationMessage || (config.editor.tabSize === 'auto');
}
let insertSpaces = DEFAULT_INDENTATION.insertSpaces;
if (config.editor && typeof config.editor.insertSpaces !== 'undefined') {
insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces));
- shouldShowMigrationMessage = shouldShowMigrationMessage || (config.editor.insertSpaces === 'auto');
}
let newDefaultEOL = this._modelCreationOptions.defaultEOL;
@@ -267,12 +255,6 @@ export class ModelServiceImpl implements IModelService {
defaultEOL: newDefaultEOL,
trimAutoWhitespace: trimAutoWhitespace
});
-
-
- if (shouldShowMigrationMessage && !this._hasShownMigrationMessage) {
- this._hasShownMigrationMessage = true;
- this._messageService.show(Severity.Info, nls.localize('indentAutoMigrate', "Please update your settings: `editor.detectIndentation` replaces `editor.tabSize`: \"auto\" or `editor.insertSpaces`: \"auto\""));
- }
};
this._configurationServiceSubscription = this._configurationService.onDidUpdateConfiguration(e => {
@@ -362,7 +344,7 @@ export class ModelServiceImpl implements IModelService {
if (this._models[modelId]) {
// There already exists a model with this id => this is a programmer error
- throw new Error('ModelService: Cannot add model ' + anonymize(modelId) + ' because it already exists!');
+ throw new Error('ModelService: Cannot add model because it already exists!');
}
let modelData = new ModelData(model, (modelData, events) => this._onModelEvents(modelData, events));
diff --git a/src/vs/editor/common/viewLayout/viewLineParts.ts b/src/vs/editor/common/viewLayout/viewLineParts.ts
index aa7694aa5e5..5baef520900 100644
--- a/src/vs/editor/common/viewLayout/viewLineParts.ts
+++ b/src/vs/editor/common/viewLayout/viewLineParts.ts
@@ -4,220 +4,95 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
-import * as strings from 'vs/base/common/strings';
-import { Range } from 'vs/editor/common/core/range';
-import { ViewLineToken, ViewLineTokens } from 'vs/editor/common/core/viewLineToken';
import { InlineDecoration } from 'vs/editor/common/viewModel/viewModel';
-import { CharCode } from 'vs/base/common/charCode';
-import { LineParts } from 'vs/editor/common/core/lineParts';
+import { Constants } from 'vs/editor/common/core/uint';
-function cmpLineDecorations(a: InlineDecoration, b: InlineDecoration): number {
- let r = Range.compareRangesUsingStarts(a.range, b.range);
- if (r === 0) {
- if (a.inlineClassName < b.inlineClassName) {
- return -1;
+export class Decoration {
+ _decorationBrand: void;
+
+ public readonly startColumn: number;
+ public readonly endColumn: number;
+ public readonly className: string;
+
+ constructor(startColumn: number, endColumn: number, className: string) {
+ this.startColumn = startColumn;
+ this.endColumn = endColumn;
+ this.className = className;
+ }
+
+ private static _equals(a: Decoration, b: Decoration): boolean {
+ return (
+ a.startColumn === b.startColumn
+ && a.endColumn === b.endColumn
+ && a.className === b.className
+ );
+ }
+
+ public static equalsArr(a: Decoration[], b: Decoration[]): boolean {
+ let aLen = a.length;
+ let bLen = b.length;
+ if (aLen !== bLen) {
+ return false;
}
- if (a.inlineClassName > b.inlineClassName) {
- return 1;
- }
- return 0;
- }
- return r;
-}
-
-export function createLineParts(lineNumber: number, minLineColumn: number, lineContent: string, tabSize: number, lineTokens: ViewLineTokens, rawLineDecorations: InlineDecoration[], renderWhitespace: 'none' | 'boundary' | 'all'): LineParts {
- if (renderWhitespace !== 'none') {
- rawLineDecorations = insertWhitespaceLineDecorations(lineNumber, lineContent, tabSize, lineTokens.getFauxIndentLength(), renderWhitespace, rawLineDecorations);
- }
-
- if (rawLineDecorations.length > 0) {
- rawLineDecorations.sort(cmpLineDecorations);
- return createViewLineParts(lineNumber, minLineColumn, lineTokens, lineContent, rawLineDecorations);
- } else {
- return createFastViewLineParts(lineTokens, lineContent);
- }
-}
-
-function trimEmptyTrailingPart(parts: ViewLineToken[], lineContent: string): ViewLineToken[] {
- if (parts.length <= 1) {
- return parts;
- }
- var lastPartStartIndex = parts[parts.length - 1].startIndex;
- if (lastPartStartIndex < lineContent.length) {
- // All is good
- return parts;
- }
- // Remove last line part
- return parts.slice(0, parts.length - 1);
-}
-
-function insertOneCustomLineDecoration(dest: InlineDecoration[], lineNumber: number, startColumn: number, endColumn: number, className: string): void {
- dest.push(new InlineDecoration(new Range(lineNumber, startColumn, lineNumber, endColumn), className));
-}
-
-function insertWhitespaceLineDecorations(lineNumber: number, lineContent: string, tabSize: number, fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'all', rawLineDecorations: InlineDecoration[]): InlineDecoration[] {
- let lineLength = lineContent.length;
- if (lineLength === fauxIndentLength) {
- return rawLineDecorations;
- }
-
- let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
- let lastNonWhitespaceIndex: number;
- if (firstNonWhitespaceIndex === -1) {
- // The entire line is whitespace
- firstNonWhitespaceIndex = lineLength;
- lastNonWhitespaceIndex = lineLength;
- } else {
- lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent);
- }
-
- let sm_endIndex: number[] = [];
- let sm_decoration: string[] = [];
-
- if (fauxIndentLength > 0) {
- // add faux indent state
- sm_endIndex.push(fauxIndentLength - 1);
- sm_decoration.push(null);
- }
- if (firstNonWhitespaceIndex > fauxIndentLength) {
- // add leading whitespace state
- sm_endIndex.push(firstNonWhitespaceIndex - 1);
- sm_decoration.push('vs-whitespace');
-
- }
-
- let startOfWhitespace = -1;
- let hasTab = false;
-
- for (let i = Math.max(firstNonWhitespaceIndex, fauxIndentLength); i <= lastNonWhitespaceIndex; ++i) {
- let currentCharIsTab = lineContent.charCodeAt(i) === CharCode.Tab;
- if (currentCharIsTab || lineContent.charCodeAt(i) === CharCode.Space) {
- if (currentCharIsTab) {
- hasTab = true;
- }
- if (startOfWhitespace === -1) {
- startOfWhitespace = i;
- }
- } else if (startOfWhitespace !== -1) {
- if (renderWhitespace === 'all' || renderWhitespace === 'boundary' && (hasTab || i - startOfWhitespace >= 2)) {
- sm_endIndex.push(startOfWhitespace - 1);
- sm_decoration.push(null);
-
- sm_endIndex.push(i - 1);
- sm_decoration.push('vs-whitespace');
- }
-
- startOfWhitespace = -1;
- hasTab = false;
- }
- }
-
- // add content state
- sm_endIndex.push(lastNonWhitespaceIndex);
- sm_decoration.push(null);
-
- // add trailing whitespace state
- sm_endIndex.push(lineLength - 1);
- sm_decoration.push('vs-whitespace');
-
- // add dummy state to avoid array length checks
- sm_endIndex.push(lineLength);
- sm_decoration.push(null);
-
- return insertCustomLineDecorationsWithStateMachine(lineNumber, lineContent, tabSize, rawLineDecorations, sm_endIndex, sm_decoration);
-}
-
-function insertCustomLineDecorationsWithStateMachine(lineNumber: number, lineContent: string, tabSize: number, rawLineDecorations: InlineDecoration[], sm_endIndex: number[], sm_decoration: string[]): InlineDecoration[] {
- let lineLength = lineContent.length;
- let currentStateIndex = 0;
- let stateEndIndex = sm_endIndex[currentStateIndex];
- let stateDecoration = sm_decoration[currentStateIndex];
-
- let result = rawLineDecorations.slice(0);
- let tmpIndent = 0;
- let whitespaceStartColumn = 1;
-
- for (let index = 0; index < lineLength; index++) {
- let chCode = lineContent.charCodeAt(index);
-
- if (chCode === CharCode.Tab) {
- tmpIndent = tabSize;
- } else {
- tmpIndent++;
- }
-
- if (index === stateEndIndex) {
- if (stateDecoration !== null) {
- insertOneCustomLineDecoration(result, lineNumber, whitespaceStartColumn, index + 2, stateDecoration);
- }
- whitespaceStartColumn = index + 2;
- tmpIndent = tmpIndent % tabSize;
-
- currentStateIndex++;
- stateEndIndex = sm_endIndex[currentStateIndex];
- stateDecoration = sm_decoration[currentStateIndex];
- } else {
- if (stateDecoration !== null && tmpIndent >= tabSize) {
- insertOneCustomLineDecoration(result, lineNumber, whitespaceStartColumn, index + 2, stateDecoration);
- whitespaceStartColumn = index + 2;
- tmpIndent = tmpIndent % tabSize;
+ for (let i = 0; i < aLen; i++) {
+ if (!Decoration._equals(a[i], b[i])) {
+ return false;
}
}
+ return true;
}
- return result;
-}
-
-function createFastViewLineParts(lineTokens: ViewLineTokens, lineContent: string): LineParts {
- let parts = lineTokens.getTokens();
- parts = trimEmptyTrailingPart(parts, lineContent);
- return new LineParts(parts, lineContent.length + 1);
-}
-
-function createViewLineParts(lineNumber: number, minLineColumn: number, lineTokens: ViewLineTokens, lineContent: string, rawLineDecorations: InlineDecoration[]): LineParts {
- // lineDecorations might overlap on top of each other, so they need to be normalized
- var lineDecorations = LineDecorationsNormalizer.normalize(lineNumber, minLineColumn, rawLineDecorations),
- lineDecorationsIndex = 0,
- lineDecorationsLength = lineDecorations.length;
-
- var actualLineTokens = lineTokens.getTokens(),
- nextStartOffset: number,
- currentTokenEndOffset: number,
- currentTokenClassName: string;
-
- var parts: ViewLineToken[] = [];
-
- for (var i = 0, len = actualLineTokens.length; i < len; i++) {
- nextStartOffset = actualLineTokens[i].startIndex;
- currentTokenEndOffset = (i + 1 < len ? actualLineTokens[i + 1].startIndex : lineTokens.getTextLength());
- currentTokenClassName = actualLineTokens[i].type;
-
- while (lineDecorationsIndex < lineDecorationsLength && lineDecorations[lineDecorationsIndex].startOffset < currentTokenEndOffset) {
- if (lineDecorations[lineDecorationsIndex].startOffset > nextStartOffset) {
- // the first decorations starts after the token
- parts.push(new ViewLineToken(nextStartOffset, currentTokenClassName));
- nextStartOffset = lineDecorations[lineDecorationsIndex].startOffset;
- }
-
- parts.push(new ViewLineToken(nextStartOffset, currentTokenClassName + ' ' + lineDecorations[lineDecorationsIndex].className));
-
- if (lineDecorations[lineDecorationsIndex].endOffset >= currentTokenEndOffset) {
- // this decoration goes on to the next token
- nextStartOffset = currentTokenEndOffset;
- break;
- } else {
- // this decorations stops inside this token
- nextStartOffset = lineDecorations[lineDecorationsIndex].endOffset + 1;
- lineDecorationsIndex++;
- }
+ public static filter(lineDecorations: InlineDecoration[], lineNumber: number, minLineColumn: number, maxLineColumn: number): Decoration[] {
+ if (lineDecorations.length === 0) {
+ return [];
}
- if (nextStartOffset < currentTokenEndOffset) {
- parts.push(new ViewLineToken(nextStartOffset, currentTokenClassName));
+ let result: Decoration[] = [], resultLen = 0;
+
+ for (let i = 0, len = lineDecorations.length; i < len; i++) {
+ let d = lineDecorations[i];
+ let range = d.range;
+ let className = d.inlineClassName;
+
+ if (range.endLineNumber < lineNumber || range.startLineNumber > lineNumber) {
+ // Ignore decorations that sit outside this line
+ continue;
+ }
+
+ if (range.isEmpty()) {
+ // Ignore empty range decorations
+ continue;
+ }
+
+ let startColumn = (range.startLineNumber === lineNumber ? range.startColumn : minLineColumn);
+ let endColumn = (range.endLineNumber === lineNumber ? range.endColumn : maxLineColumn);
+
+ if (endColumn <= 1) {
+ // An empty decoration (endColumn === 1)
+ continue;
+ }
+
+ result[resultLen++] = new Decoration(startColumn, endColumn, className);
}
+
+ return result;
}
- return new LineParts(parts, lineContent.length + 1);
+ public static compare(a: Decoration, b: Decoration): number {
+ if (a.startColumn === b.startColumn) {
+ if (a.endColumn === b.endColumn) {
+ if (a.className < b.className) {
+ return -1;
+ }
+ if (a.className > b.className) {
+ return 1;
+ }
+ return 0;
+ }
+ return a.endColumn - b.endColumn;
+ }
+ return a.startColumn - b.startColumn;
+ }
}
export class DecorationSegment {
@@ -292,63 +167,36 @@ class Stack {
}
export class LineDecorationsNormalizer {
- /**
- * A number that is guaranteed to be larger than the maximum line column
- */
- private static MAX_LINE_LENGTH = 10000000;
-
/**
* Normalize line decorations. Overlapping decorations will generate multiple segments
*/
- public static normalize(lineNumber: number, minLineColumn: number, lineDecorations: InlineDecoration[]): DecorationSegment[] {
-
- var result: DecorationSegment[] = [];
-
+ public static normalize(lineDecorations: Decoration[]): DecorationSegment[] {
if (lineDecorations.length === 0) {
- return result;
+ return [];
}
- var stack = new Stack(),
- nextStartOffset = 0,
- d: InlineDecoration,
- currentStartOffset: number,
- currentEndOffset: number,
- i: number,
- len: number;
+ let result: DecorationSegment[] = [];
- for (i = 0, len = lineDecorations.length; i < len; i++) {
- d = lineDecorations[i];
+ let stack = new Stack();
+ let nextStartOffset = 0;
- if (d.range.endLineNumber < lineNumber || d.range.startLineNumber > lineNumber) {
- // Ignore decorations that sit outside this line
- continue;
- }
+ for (let i = 0, len = lineDecorations.length; i < len; i++) {
+ let d = lineDecorations[i];
- if (d.range.startLineNumber === d.range.endLineNumber && d.range.startColumn === d.range.endColumn) {
- // Ignore empty range decorations
- continue;
- }
-
- currentStartOffset = (d.range.startLineNumber === lineNumber ? d.range.startColumn - 1 : minLineColumn - 1);
- currentEndOffset = (d.range.endLineNumber === lineNumber ? d.range.endColumn - 2 : LineDecorationsNormalizer.MAX_LINE_LENGTH - 1);
-
- if (currentEndOffset < 0) {
- // An empty decoration (endColumn === 1)
- continue;
- }
+ let currentStartOffset = d.startColumn - 1;
+ let currentEndOffset = d.endColumn - 2;
nextStartOffset = stack.consumeLowerThan(currentStartOffset, nextStartOffset, result);
if (stack.count === 0) {
nextStartOffset = currentStartOffset;
}
- stack.insert(currentEndOffset, d.inlineClassName);
+ stack.insert(currentEndOffset, d.className);
}
- stack.consumeLowerThan(LineDecorationsNormalizer.MAX_LINE_LENGTH, nextStartOffset, result);
+ stack.consumeLowerThan(Constants.MAX_SAFE_SMALL_INTEGER, nextStartOffset, result);
return result;
}
}
-
diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts
index 58621061fbc..6f27d84803f 100644
--- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts
+++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts
@@ -6,46 +6,66 @@
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { CharCode } from 'vs/base/common/charCode';
-import { LineParts } from 'vs/editor/common/core/lineParts';
+import { Decoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/viewLineParts';
+import * as strings from 'vs/base/common/strings';
+
+export const enum RenderWhitespace {
+ None = 0,
+ Boundary = 1,
+ All = 2
+}
export class RenderLineInput {
- _renderLineInputBrand: void;
- lineContent: string;
- tabSize: number;
- spaceWidth: number;
- stopRenderingLineAfter: number;
- renderWhitespace: 'none' | 'boundary' | 'all';
- renderControlCharacters: boolean;
- lineParts: LineParts;
+ public readonly lineContent: string;
+ public readonly fauxIndentLength: number;
+ public readonly lineTokens: ViewLineToken[];
+ public readonly lineDecorations: Decoration[];
+ public readonly tabSize: number;
+ public readonly spaceWidth: number;
+ public readonly stopRenderingLineAfter: number;
+ public readonly renderWhitespace: RenderWhitespace;
+ public readonly renderControlCharacters: boolean;
constructor(
lineContent: string,
+ fauxIndentLength: number,
+ lineTokens: ViewLineToken[],
+ lineDecorations: Decoration[],
tabSize: number,
spaceWidth: number,
stopRenderingLineAfter: number,
renderWhitespace: 'none' | 'boundary' | 'all',
renderControlCharacters: boolean,
- lineParts: LineParts
) {
this.lineContent = lineContent;
+ this.fauxIndentLength = fauxIndentLength;
+ this.lineTokens = lineTokens;
+ this.lineDecorations = lineDecorations;
this.tabSize = tabSize;
this.spaceWidth = spaceWidth;
this.stopRenderingLineAfter = stopRenderingLineAfter;
- this.renderWhitespace = renderWhitespace;
+ this.renderWhitespace = (
+ renderWhitespace === 'all'
+ ? RenderWhitespace.All
+ : renderWhitespace === 'boundary'
+ ? RenderWhitespace.Boundary
+ : RenderWhitespace.None
+ );
this.renderControlCharacters = renderControlCharacters;
- this.lineParts = lineParts;
}
public equals(other: RenderLineInput): boolean {
return (
this.lineContent === other.lineContent
+ && this.fauxIndentLength === other.fauxIndentLength
&& this.tabSize === other.tabSize
&& this.spaceWidth === other.spaceWidth
&& this.stopRenderingLineAfter === other.stopRenderingLineAfter
&& this.renderWhitespace === other.renderWhitespace
&& this.renderControlCharacters === other.renderControlCharacters
- && this.lineParts.equals(other.lineParts)
+ && Decoration.equalsArr(this.lineDecorations, other.lineDecorations)
+ && ViewLineToken.equalsArr(this.lineTokens, other.lineTokens)
);
}
}
@@ -166,87 +186,269 @@ export class RenderLineOutput {
readonly characterMapping: CharacterMapping;
readonly output: string;
- readonly isWhitespaceOnly: boolean;
- constructor(characterMapping: CharacterMapping, output: string, isWhitespaceOnly: boolean) {
+ constructor(characterMapping: CharacterMapping, output: string) {
this.characterMapping = characterMapping;
this.output = output;
- this.isWhitespaceOnly = isWhitespaceOnly;
}
}
-export function renderLine(input: RenderLineInput): RenderLineOutput {
- const lineText = input.lineContent;
- const lineTextLength = lineText.length;
- const tabSize = input.tabSize;
- const spaceWidth = input.spaceWidth;
- const actualLineParts = input.lineParts.parts;
- const renderWhitespace = input.renderWhitespace;
- const renderControlCharacters = input.renderControlCharacters;
- const charBreakIndex = (input.stopRenderingLineAfter === -1 ? lineTextLength : input.stopRenderingLineAfter - 1);
-
- if (lineTextLength === 0) {
+export function renderViewLine(input: RenderLineInput): RenderLineOutput {
+ if (input.lineContent.length === 0) {
return new RenderLineOutput(
new CharacterMapping(0),
// This is basically for IE's hit test to work
- ' ',
- true
+ ' '
);
}
- if (actualLineParts.length === 0) {
- throw new Error('Cannot render non empty line without line parts!');
+ return _renderLine(resolveRenderLineInput(input));
+}
+
+class ResolvedRenderLineInput {
+ constructor(
+ public readonly lineContent: string,
+ public readonly len: number,
+ public readonly isOverflowing: boolean,
+ public readonly tokens: ViewLineToken[],
+ public readonly lineDecorations: Decoration[],
+ public readonly tabSize: number,
+ public readonly spaceWidth: number,
+ public readonly renderWhitespace: RenderWhitespace,
+ public readonly renderControlCharacters: boolean,
+ ) {
+ //
+ }
+}
+
+function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput {
+ const lineContent = input.lineContent;
+
+ let isOverflowing: boolean;
+ let len: number;
+
+ if (input.stopRenderingLineAfter !== -1 && input.stopRenderingLineAfter < lineContent.length) {
+ isOverflowing = true;
+ len = input.stopRenderingLineAfter;
+ } else {
+ isOverflowing = false;
+ len = lineContent.length;
}
- return renderLineActual(lineText, lineTextLength, tabSize, spaceWidth, actualLineParts, renderWhitespace, renderControlCharacters, charBreakIndex);
+ let tokens = removeOverflowing(input.lineTokens, len);
+ if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary) {
+ tokens = _applyRenderWhitespace(lineContent, len, tokens, input.fauxIndentLength, input.tabSize, input.renderWhitespace === RenderWhitespace.Boundary);
+ }
+ if (input.lineDecorations.length > 0) {
+ tokens = _applyInlineDecorations(lineContent, len, tokens, input.lineDecorations);
+ }
+
+ return new ResolvedRenderLineInput(
+ lineContent,
+ len,
+ isOverflowing,
+ tokens,
+ input.lineDecorations,
+ input.tabSize,
+ input.spaceWidth,
+ input.renderWhitespace,
+ input.renderControlCharacters
+ );
}
-function isWhitespace(type: string): boolean {
- return (type.indexOf('vs-whitespace') >= 0);
+function removeOverflowing(tokens: ViewLineToken[], len: number): ViewLineToken[] {
+ if (tokens.length === 0) {
+ return tokens;
+ }
+ if (tokens[tokens.length - 1].endIndex === len) {
+ return tokens;
+ }
+ let result: ViewLineToken[] = [];
+ for (let tokenIndex = 0, tokensLen = tokens.length; tokenIndex < tokensLen; tokenIndex++) {
+ const endIndex = tokens[tokenIndex].endIndex;
+ if (endIndex === len) {
+ result[tokenIndex] = tokens[tokenIndex];
+ break;
+ }
+ if (endIndex > len) {
+ result[tokenIndex] = new ViewLineToken(len, tokens[tokenIndex].type);
+ break;
+ }
+ result[tokenIndex] = tokens[tokenIndex];
+ }
+ return result;
}
-function isControlCharacter(characterCode: number): boolean {
- return characterCode < 32;
-}
+function _applyRenderWhitespace(lineContent: string, len: number, tokens: ViewLineToken[], fauxIndentLength: number, tabSize: number, onlyBoundary: boolean): ViewLineToken[] {
-const _controlCharacterSequenceConversionStart = 9216;
-function controlCharacterToPrintable(characterCode: number): string {
- return String.fromCharCode(_controlCharacterSequenceConversionStart + characterCode);
-}
+ let result: ViewLineToken[] = [], resultLen = 0;
+ let tokenIndex = 0;
+ let tokenType = tokens[tokenIndex].type;
+ let tokenEndIndex = tokens[tokenIndex].endIndex;
-function renderLineActual(lineText: string, lineTextLength: number, tabSize: number, spaceWidth: number, actualLineParts: ViewLineToken[], renderWhitespace: 'none' | 'boundary' | 'all', renderControlCharacters: boolean, charBreakIndex: number): RenderLineOutput {
- lineTextLength = +lineTextLength;
- tabSize = +tabSize;
- charBreakIndex = +charBreakIndex;
+ if (fauxIndentLength > 0) {
+ result[resultLen++] = new ViewLineToken(fauxIndentLength, '');
+ }
- let charIndex = 0;
- let out = '';
- let charOffsetInPart = 0;
- let tabsCharDelta = 0;
- let isWhitespaceOnly = /^\s*$/.test(lineText);
+ let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
+ let lastNonWhitespaceIndex: number;
+ if (firstNonWhitespaceIndex === -1) {
+ // The entire line is whitespace
+ firstNonWhitespaceIndex = len;
+ lastNonWhitespaceIndex = len;
+ } else {
+ lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent);
+ }
- let characterMapping = new CharacterMapping(Math.min(lineTextLength, charBreakIndex) + 1);
+ let tmpIndent = 0;
+ for (let charIndex = 0; charIndex < fauxIndentLength; charIndex++) {
+ const chCode = lineContent.charCodeAt(charIndex);
+ if (chCode === CharCode.Tab) {
+ tmpIndent = tabSize;
+ } else {
+ tmpIndent++;
+ }
+ }
+ tmpIndent = tmpIndent % tabSize;
- out += '';
- for (let partIndex = 0, partIndexLen = actualLineParts.length; partIndex < partIndexLen; partIndex++) {
- let part = actualLineParts[partIndex];
+ let wasInWhitespace = false;
+ for (let charIndex = fauxIndentLength; charIndex < len; charIndex++) {
+ const chCode = lineContent.charCodeAt(charIndex);
- let parsRendersWhitespace = (renderWhitespace !== 'none' && isWhitespace(part.type));
-
- let toCharIndex = lineTextLength;
- if (partIndex + 1 < partIndexLen) {
- let nextPart = actualLineParts[partIndex + 1];
- toCharIndex = Math.min(lineTextLength, nextPart.startIndex);
+ let isInWhitespace: boolean;
+ if (charIndex < firstNonWhitespaceIndex || charIndex > lastNonWhitespaceIndex) {
+ // in leading or trailing whitespace
+ isInWhitespace = true;
+ } else if (chCode === CharCode.Tab) {
+ // a tab character is rendered both in all and boundary cases
+ isInWhitespace = true;
+ } else if (chCode === CharCode.Space) {
+ // hit a space character
+ if (onlyBoundary) {
+ // rendering only boundary whitespace
+ if (wasInWhitespace) {
+ isInWhitespace = true;
+ } else {
+ const nextChCode = (charIndex + 1 < len ? lineContent.charCodeAt(charIndex + 1) : CharCode.Null);
+ isInWhitespace = (nextChCode === CharCode.Space || nextChCode === CharCode.Tab);
+ }
+ } else {
+ isInWhitespace = true;
+ }
+ } else {
+ isInWhitespace = false;
}
+ if (wasInWhitespace) {
+ // was in whitespace token
+ if (!isInWhitespace || tmpIndent >= tabSize) {
+ // leaving whitespace token or entering a new indent
+ result[resultLen++] = new ViewLineToken(charIndex, 'vs-whitespace');
+ tmpIndent = tmpIndent % tabSize;
+ }
+ } else {
+ // was in regular token
+ if (charIndex === tokenEndIndex || (isInWhitespace && charIndex > fauxIndentLength)) {
+ result[resultLen++] = new ViewLineToken(charIndex, tokenType);
+ tmpIndent = tmpIndent % tabSize;
+ }
+ }
+
+ if (chCode === CharCode.Tab) {
+ tmpIndent = tabSize;
+ } else {
+ tmpIndent++;
+ }
+
+ wasInWhitespace = isInWhitespace;
+
+ if (charIndex === tokenEndIndex) {
+ tokenIndex++;
+ tokenType = tokens[tokenIndex].type;
+ tokenEndIndex = tokens[tokenIndex].endIndex;
+ }
+ }
+
+ if (wasInWhitespace) {
+ // was in whitespace token
+ result[resultLen++] = new ViewLineToken(len, 'vs-whitespace');
+ } else {
+ // was in regular token
+ result[resultLen++] = new ViewLineToken(len, tokenType);
+ }
+
+ return result;
+}
+
+function _applyInlineDecorations(lineContent: string, len: number, tokens: ViewLineToken[], _lineDecorations: Decoration[]): ViewLineToken[] {
+ _lineDecorations.sort(Decoration.compare);
+ const lineDecorations = LineDecorationsNormalizer.normalize(_lineDecorations);
+ const lineDecorationsLen = lineDecorations.length;
+
+ let lineDecorationIndex = 0;
+ let result: ViewLineToken[] = [], resultLen = 0, lastResultEndIndex = 0;
+ for (let tokenIndex = 0, len = tokens.length; tokenIndex < len; tokenIndex++) {
+ const token = tokens[tokenIndex];
+ const tokenEndIndex = token.endIndex;
+ const tokenType = token.type;
+
+ while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset < tokenEndIndex) {
+ const lineDecoration = lineDecorations[lineDecorationIndex];
+
+ if (lineDecoration.startOffset > lastResultEndIndex) {
+ lastResultEndIndex = lineDecoration.startOffset;
+ result[resultLen++] = new ViewLineToken(lastResultEndIndex, tokenType);
+ }
+
+ if (lineDecoration.endOffset + 1 < tokenEndIndex) {
+ lastResultEndIndex = lineDecoration.endOffset + 1;
+ result[resultLen++] = new ViewLineToken(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
+ lineDecorationIndex++;
+ } else {
+ break;
+ }
+ }
+
+ if (tokenEndIndex > lastResultEndIndex) {
+ lastResultEndIndex = tokenEndIndex;
+ result[resultLen++] = new ViewLineToken(lastResultEndIndex, tokenType);
+ }
+ }
+
+ return result;
+}
+
+function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
+ const lineContent = input.lineContent;
+ const len = input.len;
+ const isOverflowing = input.isOverflowing;
+ const tokens = input.tokens;
+ const tabSize = input.tabSize;
+ const spaceWidth = input.spaceWidth;
+ const renderWhitespace = input.renderWhitespace;
+ const renderControlCharacters = input.renderControlCharacters;
+
+ const characterMapping = new CharacterMapping(len + 1);
+
+ let charIndex = 0;
+ let tabsCharDelta = 0;
+ let charOffsetInPart = 0;
+
+ let out = '';
+ for (let tokenIndex = 0, tokensLen = tokens.length; tokenIndex < tokensLen; tokenIndex++) {
+ const token = tokens[tokenIndex];
+ const tokenEndIndex = token.endIndex;
+ const tokenType = token.type;
+ const tokenRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (tokenType.indexOf('vs-whitespace') >= 0));
charOffsetInPart = 0;
- if (parsRendersWhitespace) {
+
+ if (tokenRendersWhitespace) {
let partContentCnt = 0;
let partContent = '';
- for (; charIndex < toCharIndex; charIndex++) {
- characterMapping.setPartData(charIndex, partIndex, charOffsetInPart);
- let charCode = lineText.charCodeAt(charIndex);
+ for (; charIndex < tokenEndIndex; charIndex++) {
+ characterMapping.setPartData(charIndex, tokenIndex, charOffsetInPart);
+ const charCode = lineContent.charCodeAt(charIndex);
if (charCode === CharCode.Tab) {
let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
@@ -269,24 +471,16 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
}
charOffsetInPart++;
-
- if (charIndex >= charBreakIndex) {
- out += `${partContent}…`;
- characterMapping.setPartData(charIndex, partIndex, charOffsetInPart);
- return new RenderLineOutput(
- characterMapping,
- out,
- isWhitespaceOnly
- );
- }
}
- out += `${partContent}`;
- } else {
- out += ``;
- for (; charIndex < toCharIndex; charIndex++) {
- characterMapping.setPartData(charIndex, partIndex, charOffsetInPart);
- let charCode = lineText.charCodeAt(charIndex);
+ out += `${partContent}`;
+
+ } else {
+ let partContent = '';
+
+ for (; charIndex < tokenEndIndex; charIndex++) {
+ characterMapping.setPartData(charIndex, tokenIndex, charOffsetInPart);
+ const charCode = lineContent.charCodeAt(charIndex);
switch (charCode) {
case CharCode.Tab:
@@ -294,75 +488,66 @@ function renderLineActual(lineText: string, lineTextLength: number, tabSize: num
tabsCharDelta += insertSpacesCount - 1;
charOffsetInPart += insertSpacesCount - 1;
while (insertSpacesCount > 0) {
- out += ' ';
+ partContent += ' ';
insertSpacesCount--;
}
break;
case CharCode.Space:
- out += ' ';
+ partContent += ' ';
break;
case CharCode.LessThan:
- out += '<';
+ partContent += '<';
break;
case CharCode.GreaterThan:
- out += '>';
+ partContent += '>';
break;
case CharCode.Ampersand:
- out += '&';
+ partContent += '&';
break;
case CharCode.Null:
- out += '';
+ partContent += '';
break;
case CharCode.UTF8_BOM:
case CharCode.LINE_SEPARATOR_2028:
- out += '\ufffd';
+ partContent += '\ufffd';
break;
case CharCode.CarriageReturn:
// zero width space, because carriage return would introduce a line break
- out += '';
+ partContent += '';
break;
default:
- if (renderControlCharacters && isControlCharacter(charCode)) {
- out += controlCharacterToPrintable(charCode);
+ if (renderControlCharacters && charCode < 32) {
+ partContent += String.fromCharCode(9216 + charCode);
} else {
- out += lineText.charAt(charIndex);
+ partContent += String.fromCharCode(charCode);;
}
}
charOffsetInPart++;
-
- if (charIndex >= charBreakIndex) {
- out += '…';
- characterMapping.setPartData(charIndex, partIndex, charOffsetInPart);
- return new RenderLineOutput(
- characterMapping,
- out,
- isWhitespaceOnly
- );
- }
}
- out += '';
- }
+ out += `${partContent}`;
+ }
}
- out += '';
// When getting client rects for the last character, we will position the
// text range at the end of the span, insteaf of at the beginning of next span
- characterMapping.setPartData(lineTextLength, actualLineParts.length - 1, charOffsetInPart);
+ characterMapping.setPartData(len, tokens.length - 1, charOffsetInPart);
- return new RenderLineOutput(
- characterMapping,
- out,
- isWhitespaceOnly
- );
+ if (isOverflowing) {
+ out += `…`;
+ }
+
+ out += '';
+
+ return new RenderLineOutput(characterMapping, out);
}
diff --git a/src/vs/editor/common/viewModel/filteredLineTokens.ts b/src/vs/editor/common/viewModel/filteredLineTokens.ts
deleted file mode 100644
index 9aa5812f195..00000000000
--- a/src/vs/editor/common/viewModel/filteredLineTokens.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * 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 { ViewLineTokens } from 'vs/editor/common/core/viewLineToken';
-import { LineTokens } from 'vs/editor/common/core/lineTokens';
-
-export class FilteredLineTokens {
- /**
- * [startOffset; endOffset) (i.e. do not include endOffset)
- */
- public static create(original: LineTokens, startOffset: number, endOffset: number, deltaStartIndex: number): ViewLineTokens {
- let inflatedTokens = original.sliceAndInflate(startOffset, endOffset, deltaStartIndex);
- return new ViewLineTokens(
- inflatedTokens,
- deltaStartIndex,
- endOffset - startOffset + deltaStartIndex
- );
- }
-}
-
-export class IdentityFilteredLineTokens {
-
- public static create(original: LineTokens, textLength: number): ViewLineTokens {
- let inflatedTokens = original.inflate();
- return new ViewLineTokens(
- inflatedTokens,
- 0,
- textLength
- );
- }
-}
diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts
index 66edc720e9c..1715ce7ef3c 100644
--- a/src/vs/editor/common/viewModel/splitLinesCollection.ts
+++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts
@@ -8,10 +8,9 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
-import { FilteredLineTokens, IdentityFilteredLineTokens } from 'vs/editor/common/viewModel/filteredLineTokens';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { ILinesCollection } from 'vs/editor/common/viewModel/viewModelImpl';
-import { ViewLineTokens } from 'vs/editor/common/core/viewLineToken';
+import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
export class OutputPosition {
_outputPositionBrand: void;
@@ -49,7 +48,7 @@ export interface ISplitLine {
getOutputLineContent(model: IModel, myLineNumber: number, outputLineIndex: number): string;
getOutputLineMinColumn(model: IModel, myLineNumber: number, outputLineIndex: number): number;
getOutputLineMaxColumn(model: IModel, myLineNumber: number, outputLineIndex: number): number;
- getOutputLineTokens(model: IModel, myLineNumber: number, outputLineIndex: number): ViewLineTokens;
+ getOutputLineTokens(model: IModel, myLineNumber: number, outputLineIndex: number): ViewLineToken[];
getInputColumnOfOutputPosition(outputLineIndex: number, outputColumn: number): number;
getOutputPositionOfInputPosition(deltaLineNumber: number, inputColumn: number): Position;
}
@@ -87,8 +86,9 @@ class VisibleIdentitySplitLine implements ISplitLine {
return model.getLineMaxColumn(myLineNumber);
}
- public getOutputLineTokens(model: IModel, myLineNumber: number, outputLineIndex: number): ViewLineTokens {
- return IdentityFilteredLineTokens.create(model.getLineTokens(myLineNumber, true), model.getLineMaxColumn(myLineNumber) - 1);
+ public getOutputLineTokens(model: IModel, myLineNumber: number, outputLineIndex: number): ViewLineToken[] {
+ let lineTokens = model.getLineTokens(myLineNumber, true);
+ return lineTokens.inflate();
}
public getInputColumnOfOutputPosition(outputLineIndex: number, outputColumn: number): number {
@@ -133,7 +133,7 @@ class InvisibleIdentitySplitLine implements ISplitLine {
throw new Error('Not supported');
}
- public getOutputLineTokens(model: IModel, myLineNumber: number, outputLineIndex: number): ViewLineTokens {
+ public getOutputLineTokens(model: IModel, myLineNumber: number, outputLineIndex: number): ViewLineToken[] {
throw new Error('Not supported');
}
@@ -223,7 +223,7 @@ export class SplitLine implements ISplitLine {
return this.getOutputLineContent(model, myLineNumber, outputLineIndex).length + 1;
}
- public getOutputLineTokens(model: IModel, myLineNumber: number, outputLineIndex: number): ViewLineTokens {
+ public getOutputLineTokens(model: IModel, myLineNumber: number, outputLineIndex: number): ViewLineToken[] {
if (!this._isVisible) {
throw new Error('Not supported');
}
@@ -233,7 +233,9 @@ export class SplitLine implements ISplitLine {
if (outputLineIndex > 0) {
deltaStartIndex = this.wrappedIndentLength;
}
- return FilteredLineTokens.create(model.getLineTokens(myLineNumber, true), startOffset, endOffset, deltaStartIndex);
+
+ let lineTokens = model.getLineTokens(myLineNumber, true);
+ return lineTokens.sliceAndInflate(startOffset, endOffset, deltaStartIndex);
}
public getInputColumnOfOutputPosition(outputLineIndex: number, outputColumn: number): number {
@@ -691,7 +693,7 @@ export class SplitLinesCollection implements ILinesCollection {
return this.lines[lineIndex].getOutputLineMaxColumn(this.model, lineIndex + 1, remainder);
}
- public getOutputLineTokens(outputLineNumber: number): ViewLineTokens {
+ public getOutputLineTokens(outputLineNumber: number): ViewLineToken[] {
this._ensureValidState();
outputLineNumber = this._toValidOutputLineNumber(outputLineNumber);
let r = this.prefixSumComputer.getIndexOf(outputLineNumber - 1);
diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts
index 30c689fee8d..dd4aab6550c 100644
--- a/src/vs/editor/common/viewModel/viewModel.ts
+++ b/src/vs/editor/common/viewModel/viewModel.ts
@@ -6,7 +6,7 @@
import { IEventEmitter } from 'vs/base/common/eventEmitter';
import { IModelDecoration, EndOfLinePreference, IPosition } from 'vs/editor/common/editorCommon';
-import { ViewLineTokens } from 'vs/editor/common/core/viewLineToken';
+import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
@@ -31,7 +31,7 @@ export interface IViewModel extends IEventEmitter {
getLineMaxColumn(lineNumber: number): number;
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
getLineLastNonWhitespaceColumn(lineNumber: number): number;
- getLineTokens(lineNumber: number): ViewLineTokens;
+ getLineTokens(lineNumber: number): ViewLineToken[];
getDecorationsViewportData(startLineNumber: number, endLineNumber: number): IDecorationsViewportData;
getLineRenderLineNumber(lineNumber: number): string;
/**
diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts
index 62eaaf2ee3c..7d00693222e 100644
--- a/src/vs/editor/common/viewModel/viewModelImpl.ts
+++ b/src/vs/editor/common/viewModel/viewModelImpl.ts
@@ -14,7 +14,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { ViewModelCursors } from 'vs/editor/common/viewModel/viewModelCursors';
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
import { ViewModelDecoration, IDecorationsViewportData, IViewModel } from 'vs/editor/common/viewModel/viewModel';
-import { ViewLineTokens } from 'vs/editor/common/core/viewLineToken';
+import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
export interface ILinesCollection {
setTabSize(newTabSize: number, emit: (evenType: string, payload: any) => void): boolean;
@@ -30,7 +30,7 @@ export interface ILinesCollection {
getOutputIndentGuide(outputLineNumber: number): number;
getOutputLineMinColumn(outputLineNumber: number): number;
getOutputLineMaxColumn(outputLineNumber: number): number;
- getOutputLineTokens(outputLineNumber: number): ViewLineTokens;
+ getOutputLineTokens(outputLineNumber: number): ViewLineToken[];
convertOutputPositionToInputPosition(viewLineNumber: number, viewColumn: number): Position;
convertInputPositionToOutputPosition(inputLineNumber: number, inputColumn: number): Position;
setHiddenAreas(ranges: editorCommon.IRange[], emit: (evenType: string, payload: any) => void): void;
@@ -445,7 +445,7 @@ export class ViewModel extends EventEmitter implements IViewModel {
return result + 2;
}
- public getLineTokens(lineNumber: number): ViewLineTokens {
+ public getLineTokens(lineNumber: number): ViewLineToken[] {
return this.lines.getOutputLineTokens(lineNumber);
}
diff --git a/src/vs/editor/contrib/find/common/findModel.ts b/src/vs/editor/contrib/find/common/findModel.ts
index 337a5a0fbb1..b99cd6865cf 100644
--- a/src/vs/editor/contrib/find/common/findModel.ts
+++ b/src/vs/editor/contrib/find/common/findModel.ts
@@ -17,7 +17,6 @@ import { ReplaceAllCommand } from './replaceAllCommand';
import { Selection } from 'vs/editor/common/core/selection';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IKeybindings } from 'vs/platform/keybinding/common/keybinding';
-import { SearchParams } from 'vs/editor/common/model/textModelSearch';
export const ToggleCaseSensitiveKeybinding: IKeybindings = {
primary: KeyMod.Alt | KeyCode.KEY_C,
diff --git a/src/vs/editor/contrib/find/common/replacePattern.ts b/src/vs/editor/contrib/find/common/replacePattern.ts
index 1a0bba7527e..125e363a768 100644
--- a/src/vs/editor/contrib/find/common/replacePattern.ts
+++ b/src/vs/editor/contrib/find/common/replacePattern.ts
@@ -3,8 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import * as strings from 'vs/base/common/strings';
-import { IPatternInfo } from 'vs/platform/search/common/search';
import { CharCode } from 'vs/base/common/charCode';
export class ReplacePattern {
diff --git a/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts b/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts
index 09e18e1c90d..3180bb7c383 100644
--- a/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts
+++ b/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts
@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
-import { IModelDecoration, IRange } from 'vs/editor/common/editorCommon';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { HoverOperation, IHoverComputer } from './hoverOperation';
import { GlyphHoverWidget } from './hoverWidgets';
@@ -16,11 +15,10 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModeService } from 'vs/editor/common/services/modeService';
import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer';
+import { MarkedString } from 'vs/base/common/htmlContent';
export interface IHoverMessage {
- value?: string;
- range?: IRange;
- className?: string;
+ value: MarkedString;
}
class MarginComputer implements IHoverComputer {
@@ -44,19 +42,35 @@ class MarginComputer implements IHoverComputer {
}
public computeSync(): IHoverMessage[] {
- var result: IHoverMessage[] = [],
- lineDecorations = this._editor.getLineDecorations(this._lineNumber),
- i: number,
- len: number,
- d: IModelDecoration;
+ const hasHoverContent = (contents: MarkedString | MarkedString[]) => {
+ return contents && (!Array.isArray(contents) || (contents).length > 0);
+ };
+ const toHoverMessage = (contents: MarkedString): IHoverMessage => {
+ return {
+ value: contents
+ };
+ };
- for (i = 0, len = lineDecorations.length; i < len; i++) {
- d = lineDecorations[i];
+ let lineDecorations = this._editor.getLineDecorations(this._lineNumber);
- if (d.options.glyphMarginClassName && d.options.glyphMarginHoverMessage) {
- result.push({
- value: d.options.glyphMarginHoverMessage
- });
+ let result: IHoverMessage[] = [];
+ for (let i = 0, len = lineDecorations.length; i < len; i++) {
+ let d = lineDecorations[i];
+
+ if (!d.options.glyphMarginClassName) {
+ continue;
+ }
+
+ let hoverMessage = d.options.glyphMarginHoverMessage;
+
+ if (!hasHoverContent(hoverMessage)) {
+ continue;
+ }
+
+ if (Array.isArray(hoverMessage)) {
+ result = result.concat(hoverMessage.map(toHoverMessage));
+ } else {
+ result.push(toHoverMessage(hoverMessage));
}
}
diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css
index f94bfa6e862..dbece1317f4 100644
--- a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css
+++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.css
@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-
.monaco-editor .lightbulb-glyph {
display: flex;
align-items: center;
@@ -12,6 +11,11 @@
width: 16px;
}
+.monaco-editor .lightbulb-glyph.hidden {
+ display: none;
+ visibility: hidden;
+}
+
.monaco-editor .lightbulb-glyph:hover {
cursor: pointer;
}
diff --git a/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts b/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts
index 7e7ad380507..f0b02563de4 100644
--- a/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts
+++ b/src/vs/editor/contrib/smartSelect/test/common/tokenSelectionSupport.test.ts
@@ -68,7 +68,7 @@ suite('TokenSelectionSupport', () => {
let mode: MockJSMode = null;
setup(() => {
- modelService = new ModelServiceImpl(null, new TestConfigurationService(), null);
+ modelService = new ModelServiceImpl(null, new TestConfigurationService());
tokenSelectionSupport = new TokenSelectionSupport(modelService);
mode = new MockJSMode();
});
diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts
index 1b9332f32aa..5cbf0f0c32a 100644
--- a/src/vs/editor/contrib/suggest/browser/suggestController.ts
+++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts
@@ -105,9 +105,14 @@ export class SuggestController implements IEditorContribution {
// Wire up logic to accept a suggestion on certain characters
const autoAcceptOracle = new AcceptOnCharacterOracle(editor, this.widget, item => this.onDidSelectItem(item));
this.toDispose.push(
+ autoAcceptOracle,
this.model.onDidCancel(autoAcceptOracle.reset, autoAcceptOracle),
this.model.onDidTrigger(autoAcceptOracle.reset, autoAcceptOracle),
- autoAcceptOracle
+ this.model.onDidSuggest(e => {
+ if (e.completionModel.items.length === 0) {
+ autoAcceptOracle.reset();
+ }
+ })
);
}
diff --git a/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts
index 7304fc44d36..6143d956fdd 100644
--- a/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts
+++ b/src/vs/editor/contrib/suggest/test/common/suggestModel.test.ts
@@ -18,7 +18,8 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
function createMockEditor(model: Model): MockCodeEditor {
const contextKeyService = new MockKeybindingService();
diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts
index 906f40bf5d4..2abbab025a6 100644
--- a/src/vs/editor/test/browser/controller/imeTester.ts
+++ b/src/vs/editor/test/browser/controller/imeTester.ts
@@ -124,7 +124,7 @@ function doCreateTest(strategy: TextAreaStrategy, description: string, inputStr:
cursorOffset = off;
cursorLength = len;
handler.setCursorSelections(new Range(1, 1 + cursorOffset, 1, 1 + cursorOffset + cursorLength), []);
- handler.writePlaceholderAndSelectTextAreaSync();
+ handler.focusTextArea();
};
let updateModelAndPosition = (text: string, off: number, len: number) => {
diff --git a/src/vs/editor/test/browser/standalone/simpleServices.test.ts b/src/vs/editor/test/browser/standalone/simpleServices.test.ts
index d520bfff289..e69842d211f 100644
--- a/src/vs/editor/test/browser/standalone/simpleServices.test.ts
+++ b/src/vs/editor/test/browser/standalone/simpleServices.test.ts
@@ -6,7 +6,7 @@
import * as assert from 'assert';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
-import { SimpleConfigurationService, SimpleMessageService, SimpleExtensionService, StandaloneKeybindingService, StandaloneCommandService } from 'vs/editor/browser/standalone/simpleServices';
+import { SimpleConfigurationService, SimpleMessageService, StandaloneKeybindingService, StandaloneCommandService } from 'vs/editor/browser/standalone/simpleServices';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { Keybinding, KeyCode } from 'vs/base/common/keyCodes';
@@ -32,9 +32,7 @@ suite('StandaloneKeybindingService', () => {
let contextKeyService = new ContextKeyService(configurationService);
- let extensionService = new SimpleExtensionService();
-
- let commandService = new StandaloneCommandService(instantiationService, extensionService);
+ let commandService = new StandaloneCommandService(instantiationService);
let messageService = new SimpleMessageService();
diff --git a/src/vs/editor/test/common/model/model.line.test.ts b/src/vs/editor/test/common/model/model.line.test.ts
index 0210b17139e..49675d80ccd 100644
--- a/src/vs/editor/test/common/model/model.line.test.ts
+++ b/src/vs/editor/test/common/model/model.line.test.ts
@@ -9,15 +9,12 @@ import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { ModelLine, ILineEdit, LineMarker, MarkersTracker } from 'vs/editor/common/model/modelLine';
import { MetadataConsts } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
+import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
-function assertLineTokens(actual: LineTokens, expected: TestToken[]): void {
- let inflatedActual = actual.inflate();
- assert.deepEqual(inflatedActual, expected.map((token) => {
- return {
- startIndex: token.startOffset,
- type: 'mtk' + token.color
- };
- }), 'Line tokens are equal');
+function assertLineTokens(_actual: LineTokens, _expected: TestToken[]): void {
+ let expected = TokenMetadata.inflateArr(TestToken.toTokens(_expected), _actual.getLineLength());
+ let actual = _actual.inflate();
+ assert.deepEqual(actual, expected);
}
const NO_TAB_SIZE = 0;
diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts
index 35032616b4a..4f38ca260e6 100644
--- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts
+++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts
@@ -293,18 +293,18 @@ suite('TextModelWithTokens regression tests', () => {
let model = Model.createFromString('A model with\ntwo lines');
- assertViewLineTokens(model, 1, true, [new ViewLineToken(0, 'mtk1')]);
- assertViewLineTokens(model, 2, true, [new ViewLineToken(0, 'mtk1')]);
+ assertViewLineTokens(model, 1, true, [new ViewLineToken(12, 'mtk1')]);
+ assertViewLineTokens(model, 2, true, [new ViewLineToken(9, 'mtk1')]);
model.setMode(languageIdentifier1);
- assertViewLineTokens(model, 1, true, [new ViewLineToken(0, 'mtk11')]);
- assertViewLineTokens(model, 2, true, [new ViewLineToken(0, 'mtk12')]);
+ assertViewLineTokens(model, 1, true, [new ViewLineToken(12, 'mtk11')]);
+ assertViewLineTokens(model, 2, true, [new ViewLineToken(9, 'mtk12')]);
model.setMode(languageIdentifier2);
- assertViewLineTokens(model, 1, false, [new ViewLineToken(0, 'mtk1')]);
- assertViewLineTokens(model, 2, false, [new ViewLineToken(0, 'mtk1')]);
+ assertViewLineTokens(model, 1, false, [new ViewLineToken(12, 'mtk1')]);
+ assertViewLineTokens(model, 2, false, [new ViewLineToken(9, 'mtk1')]);
model.dispose();
registration1.dispose();
diff --git a/src/vs/editor/test/common/viewLayout/viewLineParts.test.ts b/src/vs/editor/test/common/viewLayout/viewLineParts.test.ts
index e782cd87527..8057407ddeb 100644
--- a/src/vs/editor/test/common/viewLayout/viewLineParts.test.ts
+++ b/src/vs/editor/test/common/viewLayout/viewLineParts.test.ts
@@ -5,12 +5,11 @@
'use strict';
import * as assert from 'assert';
-import { DecorationSegment, LineDecorationsNormalizer, createLineParts } from 'vs/editor/common/viewLayout/viewLineParts';
+import { DecorationSegment, LineDecorationsNormalizer, Decoration } from 'vs/editor/common/viewLayout/viewLineParts';
import { Range } from 'vs/editor/common/core/range';
-import { RenderLineInput, renderLine } from 'vs/editor/common/viewLayout/viewLineRenderer';
-import { ViewLineToken, ViewLineTokens } from 'vs/editor/common/core/viewLineToken';
+import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer';
+import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { InlineDecoration } from 'vs/editor/common/viewModel/viewModel';
-import { LineParts } from 'vs/editor/common/core/lineParts';
suite('Editor ViewLayout - ViewLineParts', () => {
@@ -20,9 +19,9 @@ suite('Editor ViewLayout - ViewLineParts', () => {
test('Bug 9827:Overlapping inline decorations can cause wrong inline class to be applied', () => {
- var result = LineDecorationsNormalizer.normalize(1, 1, [
- newDecoration(1, 1, 1, 11, 'c1'),
- newDecoration(1, 3, 1, 4, 'c2')
+ var result = LineDecorationsNormalizer.normalize([
+ new Decoration(1, 11, 'c1'),
+ new Decoration(3, 4, 'c2')
]);
assert.deepEqual(result, [
@@ -34,9 +33,9 @@ suite('Editor ViewLayout - ViewLineParts', () => {
test('issue #3462: no whitespace shown at the end of a decorated line', () => {
- var result = LineDecorationsNormalizer.normalize(3, 1, [
- newDecoration(3, 15, 3, 21, 'vs-whitespace'),
- newDecoration(3, 20, 3, 21, 'inline-folded'),
+ var result = LineDecorationsNormalizer.normalize([
+ new Decoration(15, 21, 'vs-whitespace'),
+ new Decoration(20, 21, 'inline-folded'),
]);
assert.deepEqual(result, [
@@ -47,128 +46,149 @@ suite('Editor ViewLayout - ViewLineParts', () => {
test('issue #3661: Link decoration bleeds to next line when wrapping', () => {
- var result = LineDecorationsNormalizer.normalize(3, 12, [
+ let result = Decoration.filter([
newDecoration(2, 12, 3, 30, 'detected-link')
- ]);
+ ], 3, 12, 500);
assert.deepEqual(result, [
- new DecorationSegment(11, 28, 'detected-link'),
+ new Decoration(12, 30, 'detected-link'),
]);
});
- function testCreateLineParts(lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'all', expected: ViewLineToken[]): void {
- let lineParts = createLineParts(1, 1, lineContent, 4, new ViewLineTokens(tokens, fauxIndentLength, lineContent.length), [], renderWhitespace);
- let actual = lineParts.parts;
+ function testCreateLineParts(lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'all', expected: string): void {
+ let actual = renderViewLine(new RenderLineInput(
+ lineContent,
+ fauxIndentLength,
+ tokens,
+ [],
+ 4,
+ 10,
+ -1,
+ renderWhitespace,
+ false
+ ));
- assert.deepEqual(actual, expected);
+ assert.deepEqual(actual.output.split(/> {
testCreateLineParts(
'Hello world!',
[
- new ViewLineToken(0, '')
+ new ViewLineToken(12, '')
],
0,
'none',
[
- new ViewLineToken(0, '')
- ]
+ '',
+ 'Hello world!',
+ '',
+ ].join('')
);
});
test('createLineParts simple two tokens', () => {
testCreateLineParts(
'Hello world!',
[
- new ViewLineToken(0, 'a'),
- new ViewLineToken(6, 'b')
+ new ViewLineToken(6, 'a'),
+ new ViewLineToken(12, 'b')
],
0,
'none',
[
- new ViewLineToken(0, 'a'),
- new ViewLineToken(6, 'b')
- ]
+ '',
+ 'Hello ',
+ 'world!',
+ '',
+ ].join('')
);
});
test('createLineParts render whitespace - 4 leading spaces', () => {
testCreateLineParts(
' Hello world! ',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(4, 'a'),
- new ViewLineToken(6, 'b')
+ new ViewLineToken(4, ''),
+ new ViewLineToken(6, 'a'),
+ new ViewLineToken(20, 'b')
],
0,
'boundary',
[
- new ViewLineToken(0, ' vs-whitespace'),
- new ViewLineToken(4, 'a'),
- new ViewLineToken(6, 'b'),
- new ViewLineToken(16, 'b vs-whitespace')
- ]
+ '',
+ '····',
+ 'He',
+ 'llo world!',
+ '····',
+ '',
+ ].join('')
);
});
test('createLineParts render whitespace - 8 leading spaces', () => {
testCreateLineParts(
' Hello world! ',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(8, 'a'),
- new ViewLineToken(10, 'b')
+ new ViewLineToken(8, ''),
+ new ViewLineToken(10, 'a'),
+ new ViewLineToken(28, 'b')
],
0,
'boundary',
[
- new ViewLineToken(0, ' vs-whitespace'),
- new ViewLineToken(4, ' vs-whitespace'),
- new ViewLineToken(8, 'a'),
- new ViewLineToken(10, 'b'),
- new ViewLineToken(20, 'b vs-whitespace'),
- new ViewLineToken(24, 'b vs-whitespace'),
- ]
+ '',
+ '····',
+ '····',
+ 'He',
+ 'llo world!',
+ '····',
+ '····',
+ '',
+ ].join('')
);
});
test('createLineParts render whitespace - 2 leading tabs', () => {
testCreateLineParts(
'\t\tHello world!\t',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(2, 'a'),
- new ViewLineToken(4, 'b')
+ new ViewLineToken(2, ''),
+ new ViewLineToken(4, 'a'),
+ new ViewLineToken(15, 'b')
],
0,
'boundary',
[
- new ViewLineToken(0, ' vs-whitespace'),
- new ViewLineToken(1, ' vs-whitespace'),
- new ViewLineToken(2, 'a'),
- new ViewLineToken(4, 'b'),
- new ViewLineToken(14, 'b vs-whitespace'),
- ]
+ '',
+ '→ ',
+ '→ ',
+ 'He',
+ 'llo world!',
+ '→ ',
+ '',
+ ].join('')
);
});
test('createLineParts render whitespace - mixed leading spaces and tabs', () => {
testCreateLineParts(
' \t\t Hello world! \t \t \t ',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(6, 'a'),
- new ViewLineToken(8, 'b')
+ new ViewLineToken(6, ''),
+ new ViewLineToken(8, 'a'),
+ new ViewLineToken(31, 'b')
],
0,
'boundary',
[
- new ViewLineToken(0, ' vs-whitespace'),
- new ViewLineToken(3, ' vs-whitespace'),
- new ViewLineToken(4, ' vs-whitespace'),
- new ViewLineToken(6, 'a'),
- new ViewLineToken(8, 'b'),
- new ViewLineToken(18, 'b vs-whitespace'),
- new ViewLineToken(20, 'b vs-whitespace'),
- new ViewLineToken(23, 'b vs-whitespace'),
- new ViewLineToken(27, 'b vs-whitespace'),
- ]
+ '',
+ '··→ ',
+ '→ ',
+ '··',
+ 'He',
+ 'llo world!',
+ '·→',
+ '··→ ',
+ '···→',
+ '····',
+ '',
+ ].join('')
);
});
@@ -176,22 +196,24 @@ suite('Editor ViewLayout - ViewLineParts', () => {
testCreateLineParts(
'\t\t Hello world! \t \t \t ',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(4, 'a'),
- new ViewLineToken(6, 'b')
+ new ViewLineToken(4, ''),
+ new ViewLineToken(6, 'a'),
+ new ViewLineToken(29, 'b')
],
2,
'boundary',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(2, ' vs-whitespace'),
- new ViewLineToken(4, 'a'),
- new ViewLineToken(6, 'b'),
- new ViewLineToken(16, 'b vs-whitespace'),
- new ViewLineToken(18, 'b vs-whitespace'),
- new ViewLineToken(21, 'b vs-whitespace'),
- new ViewLineToken(25, 'b vs-whitespace'),
- ]
+ '',
+ ' ',
+ '··',
+ 'He',
+ 'llo world!',
+ '·→',
+ '··→ ',
+ '···→',
+ '····',
+ '',
+ ].join('')
);
});
@@ -199,21 +221,23 @@ suite('Editor ViewLayout - ViewLineParts', () => {
testCreateLineParts(
'it it it it',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(6, 'a'),
- new ViewLineToken(7, 'b')
+ new ViewLineToken(6, ''),
+ new ViewLineToken(7, 'a'),
+ new ViewLineToken(13, 'b')
],
0,
'boundary',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(2, ' vs-whitespace'),
- new ViewLineToken(4, ''),
- new ViewLineToken(6, 'a'),
- new ViewLineToken(7, 'b'),
- new ViewLineToken(9, 'b vs-whitespace'),
- new ViewLineToken(11, 'b'),
- ]
+ '',
+ 'it',
+ '··',
+ 'it',
+ ' ',
+ 'it',
+ '··',
+ 'it',
+ '',
+ ].join('')
);
});
@@ -221,37 +245,41 @@ suite('Editor ViewLayout - ViewLineParts', () => {
testCreateLineParts(
' Hello world!\t',
[
- new ViewLineToken(0, ''),
- new ViewLineToken(4, 'a'),
- new ViewLineToken(6, 'b')
+ new ViewLineToken(4, ''),
+ new ViewLineToken(6, 'a'),
+ new ViewLineToken(14, 'b')
],
0,
'all',
[
- new ViewLineToken(0, ' vs-whitespace'),
- new ViewLineToken(1, ''),
- new ViewLineToken(4, 'a'),
- new ViewLineToken(6, 'b vs-whitespace'),
- new ViewLineToken(7, 'b'),
- new ViewLineToken(13, 'b vs-whitespace'),
- ]
+ '',
+ '·',
+ 'Hel',
+ 'lo',
+ '·',
+ 'world!',
+ '→ ',
+ '',
+ ].join('')
);
});
test('createLineParts can handle unsorted inline decorations', () => {
- let lineParts = createLineParts(
- 1,
- 1,
+ let actual = renderViewLine(new RenderLineInput(
'Hello world',
- 4,
- new ViewLineTokens([new ViewLineToken(0, '')], 0, 'Hello world'.length),
+ 0,
+ [new ViewLineToken(11, '')],
[
- new InlineDecoration(new Range(1, 5, 1, 7), 'a'),
- new InlineDecoration(new Range(1, 1, 1, 3), 'b'),
- new InlineDecoration(new Range(1, 2, 1, 8), 'c'),
+ new Decoration(5, 7, 'a'),
+ new Decoration(1, 3, 'b'),
+ new Decoration(2, 8, 'c'),
],
- 'none'
- );
+ 4,
+ 10,
+ -1,
+ 'none',
+ false
+ ));
// 01234567890
// Hello world
@@ -259,78 +287,80 @@ suite('Editor ViewLayout - ViewLineParts', () => {
// bb---------
// -cccccc----
- assert.deepEqual(lineParts.parts, [
- new ViewLineToken(0, ' b'),
- new ViewLineToken(1, ' b c'),
- new ViewLineToken(2, ' c'),
- new ViewLineToken(4, ' a c'),
- new ViewLineToken(6, ' c'),
- new ViewLineToken(7, ''),
- ]);
+ assert.deepEqual(actual.output, [
+ '',
+ 'H',
+ 'e',
+ 'll',
+ 'o ',
+ 'w',
+ 'orld',
+ '',
+ ].join(''));
});
test('ViewLineParts', () => {
- assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
- newDecoration(1, 1, 1, 2, 'c1'),
- newDecoration(1, 3, 1, 4, 'c2')
+ assert.deepEqual(LineDecorationsNormalizer.normalize([
+ new Decoration(1, 2, 'c1'),
+ new Decoration(3, 4, 'c2')
]), [
new DecorationSegment(0, 0, 'c1'),
new DecorationSegment(2, 2, 'c2')
]);
- assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
- newDecoration(1, 1, 1, 3, 'c1'),
- newDecoration(1, 3, 1, 4, 'c2')
+ assert.deepEqual(LineDecorationsNormalizer.normalize([
+ new Decoration(1, 3, 'c1'),
+ new Decoration(3, 4, 'c2')
]), [
new DecorationSegment(0, 1, 'c1'),
new DecorationSegment(2, 2, 'c2')
]);
- assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
- newDecoration(1, 1, 1, 4, 'c1'),
- newDecoration(1, 3, 1, 4, 'c2')
+ assert.deepEqual(LineDecorationsNormalizer.normalize([
+ new Decoration(1, 4, 'c1'),
+ new Decoration(3, 4, 'c2')
]), [
new DecorationSegment(0, 1, 'c1'),
new DecorationSegment(2, 2, 'c1 c2')
]);
- assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
- newDecoration(1, 1, 1, 4, 'c1'),
- newDecoration(1, 1, 1, 4, 'c1*'),
- newDecoration(1, 3, 1, 4, 'c2')
+ assert.deepEqual(LineDecorationsNormalizer.normalize([
+ new Decoration(1, 4, 'c1'),
+ new Decoration(1, 4, 'c1*'),
+ new Decoration(3, 4, 'c2')
]), [
new DecorationSegment(0, 1, 'c1 c1*'),
new DecorationSegment(2, 2, 'c1 c1* c2')
]);
- assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
- newDecoration(1, 1, 1, 4, 'c1'),
- newDecoration(1, 1, 1, 4, 'c1*'),
- newDecoration(1, 1, 1, 4, 'c1**'),
- newDecoration(1, 3, 1, 4, 'c2')
+ assert.deepEqual(LineDecorationsNormalizer.normalize([
+ new Decoration(1, 4, 'c1'),
+ new Decoration(1, 4, 'c1*'),
+ new Decoration(1, 4, 'c1**'),
+ new Decoration(3, 4, 'c2')
]), [
new DecorationSegment(0, 1, 'c1 c1* c1**'),
new DecorationSegment(2, 2, 'c1 c1* c1** c2')
]);
- assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
- newDecoration(1, 1, 1, 4, 'c1'),
- newDecoration(1, 1, 1, 4, 'c1*'),
- newDecoration(1, 1, 1, 4, 'c1**'),
- newDecoration(1, 3, 1, 4, 'c2'),
- newDecoration(1, 3, 1, 4, 'c2*')
+ assert.deepEqual(LineDecorationsNormalizer.normalize([
+ new Decoration(1, 4, 'c1'),
+ new Decoration(1, 4, 'c1*'),
+ new Decoration(1, 4, 'c1**'),
+ new Decoration(3, 4, 'c2'),
+ new Decoration(3, 4, 'c2*')
]), [
new DecorationSegment(0, 1, 'c1 c1* c1**'),
new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*')
]);
- assert.deepEqual(LineDecorationsNormalizer.normalize(1, 1, [
- newDecoration(1, 1, 1, 4, 'c1'),
- newDecoration(1, 1, 1, 4, 'c1*'),
- newDecoration(1, 1, 1, 4, 'c1**'),
- newDecoration(1, 3, 1, 4, 'c2'),
- newDecoration(1, 3, 1, 5, 'c2*')
+ assert.deepEqual(LineDecorationsNormalizer.normalize([
+ new Decoration(1, 4, 'c1'),
+ new Decoration(1, 4, 'c1*'),
+ new Decoration(1, 4, 'c1**'),
+ new Decoration(3, 4, 'c2'),
+ new Decoration(3, 5, 'c2*')
]), [
new DecorationSegment(0, 1, 'c1 c1* c1**'),
new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*'),
@@ -339,7 +369,17 @@ suite('Editor ViewLayout - ViewLineParts', () => {
});
function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: ViewLineToken[]): (partIndex: number, partLength: number, offset: number, expected: number) => void {
- let renderLineOutput = renderLine(new RenderLineInput(lineContent, tabSize, 10, -1, 'none', false, new LineParts(parts, lineContent.length + 1)));
+ let renderLineOutput = renderViewLine(new RenderLineInput(
+ lineContent,
+ 0,
+ parts,
+ [],
+ tabSize,
+ 10,
+ -1,
+ 'none',
+ false
+ ));
return (partIndex: number, partLength: number, offset: number, expected: number) => {
let charOffset = renderLineOutput.characterMapping.partDataToCharOffset(partIndex, partLength, offset);
@@ -353,7 +393,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
'hello world',
4,
[
- new ViewLineToken(0, 'aToken')
+ new ViewLineToken(11, 'aToken')
]
);
testGetColumnOfLinePartOffset(0, 11, 0, 1);
@@ -375,12 +415,12 @@ suite('Editor ViewLayout - ViewLineParts', () => {
'var x = 3;',
4,
[
- new ViewLineToken(0, 'meta type js storage var expr'),
- new ViewLineToken(3, 'meta js var expr'),
- new ViewLineToken(4, 'meta js var expr var-single-variable variable'),
- new ViewLineToken(5, 'meta js var expr var-single-variable'),
- new ViewLineToken(8, 'meta js var expr var-single-variable constant numeric'),
- new ViewLineToken(9, ''),
+ new ViewLineToken(3, 'meta type js storage var expr'),
+ new ViewLineToken(4, 'meta js var expr'),
+ new ViewLineToken(5, 'meta js var expr var-single-variable variable'),
+ new ViewLineToken(8, 'meta js var expr var-single-variable'),
+ new ViewLineToken(9, 'meta js var expr var-single-variable constant numeric'),
+ new ViewLineToken(10, ''),
]
);
testGetColumnOfLinePartOffset(0, 3, 0, 1);
@@ -406,7 +446,7 @@ suite('Editor ViewLayout - ViewLineParts', () => {
'\t',
6,
[
- new ViewLineToken(0, 'vs-whitespace')
+ new ViewLineToken(1, 'vs-whitespace')
]
);
testGetColumnOfLinePartOffset(0, 6, 0, 1);
@@ -423,8 +463,8 @@ suite('Editor ViewLayout - ViewLineParts', () => {
'\tfunction',
4,
[
- new ViewLineToken(0, ''),
- new ViewLineToken(1, 'meta type js function storage'),
+ new ViewLineToken(1, ''),
+ new ViewLineToken(9, 'meta type js function storage'),
]
);
testGetColumnOfLinePartOffset(0, 4, 0, 1);
@@ -448,8 +488,8 @@ suite('Editor ViewLayout - ViewLineParts', () => {
'\t\tfunction',
4,
[
- new ViewLineToken(0, ''),
- new ViewLineToken(2, 'meta type js function storage'),
+ new ViewLineToken(2, ''),
+ new ViewLineToken(10, 'meta type js function storage'),
]
);
testGetColumnOfLinePartOffset(0, 8, 0, 1);
diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts
index 65333a15943..fa0c12db006 100644
--- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts
+++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts
@@ -5,26 +5,27 @@
'use strict';
import * as assert from 'assert';
-import { renderLine, RenderLineInput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer';
+import { renderViewLine, RenderLineInput, CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
import { CharCode } from 'vs/base/common/charCode';
-import { LineParts } from 'vs/editor/common/core/lineParts';
suite('viewLineRenderer.renderLine', () => {
- function createPart(startIndex: number, type: string): ViewLineToken {
- return new ViewLineToken(startIndex, type);
+ function createPart(endIndex: number, type: string): ViewLineToken {
+ return new ViewLineToken(endIndex, type);
}
function assertCharacterReplacement(lineContent: string, tabSize: number, expected: string, expectedCharOffsetInPart: number[][]): void {
- let _actual = renderLine(new RenderLineInput(
+ let _actual = renderViewLine(new RenderLineInput(
lineContent,
+ 0,
+ [new ViewLineToken(lineContent.length, '')],
+ [],
tabSize,
0,
-1,
'none',
- false,
- new LineParts([createPart(0, '')], lineContent.length + 1)
+ false
));
assert.equal(_actual.output, '' + expected + '');
@@ -59,14 +60,16 @@ suite('viewLineRenderer.renderLine', () => {
});
function assertParts(lineContent: string, tabSize: number, parts: ViewLineToken[], expected: string, expectedCharOffsetInPart: number[][]): void {
- let _actual = renderLine(new RenderLineInput(
+ let _actual = renderViewLine(new RenderLineInput(
lineContent,
+ 0,
+ parts,
+ [],
tabSize,
0,
-1,
'none',
- false,
- new LineParts(parts, lineContent.length + 1)
+ false
));
assert.equal(_actual.output, '' + expected + '');
@@ -78,42 +81,41 @@ suite('viewLineRenderer.renderLine', () => {
});
test('uses part type', () => {
- assertParts('x', 4, [createPart(0, 'y')], 'x', [[0, 1]]);
- assertParts('x', 4, [createPart(0, 'aAbBzZ0123456789-cC')], 'x', [[0, 1]]);
- assertParts('x', 4, [createPart(0, ' ')], 'x', [[0, 1]]);
+ assertParts('x', 4, [createPart(1, 'y')], 'x', [[0, 1]]);
+ assertParts('x', 4, [createPart(1, 'aAbBzZ0123456789-cC')], 'x', [[0, 1]]);
+ assertParts('x', 4, [createPart(1, ' ')], 'x', [[0, 1]]);
});
test('two parts', () => {
- assertParts('xy', 4, [createPart(0, 'a'), createPart(1, 'b')], 'xy', [[0], [0, 1]]);
- assertParts('xyz', 4, [createPart(0, 'a'), createPart(1, 'b')], 'xyz', [[0], [0, 1, 2]]);
- assertParts('xyz', 4, [createPart(0, 'a'), createPart(2, 'b')], 'xyz', [[0, 1], [0, 1]]);
+ assertParts('xy', 4, [createPart(1, 'a'), createPart(2, 'b')], 'xy', [[0], [0, 1]]);
+ assertParts('xyz', 4, [createPart(1, 'a'), createPart(3, 'b')], 'xyz', [[0], [0, 1, 2]]);
+ assertParts('xyz', 4, [createPart(2, 'a'), createPart(3, 'b')], 'xyz', [[0, 1], [0, 1]]);
});
test('overflow', () => {
- let _actual = renderLine(new RenderLineInput(
+ let _actual = renderViewLine(new RenderLineInput(
'Hello world!',
+ 0,
+ [
+ createPart(1, '0'),
+ createPart(2, '1'),
+ createPart(3, '2'),
+ createPart(4, '3'),
+ createPart(5, '4'),
+ createPart(6, '5'),
+ createPart(7, '6'),
+ createPart(8, '7'),
+ createPart(9, '8'),
+ createPart(10, '9'),
+ createPart(11, '10'),
+ createPart(12, '11'),
+ ],
+ [],
4,
10,
6,
'boundary',
- false,
- new LineParts(
- [
- createPart(0, '0'),
- createPart(1, '1'),
- createPart(2, '2'),
- createPart(3, '3'),
- createPart(4, '4'),
- createPart(5, '5'),
- createPart(6, '6'),
- createPart(7, '7'),
- createPart(8, '8'),
- createPart(9, '9'),
- createPart(10, '10'),
- createPart(11, '11'),
- ],
- 'Hello world!'.length + 1
- )
+ false
));
let expectedOutput = [
@@ -122,7 +124,8 @@ suite('viewLineRenderer.renderLine', () => {
'l',
'l',
'o',
- ' …'
+ ' ',
+ '…'
].join('');
assert.equal(_actual.output, '' + expectedOutput + '');
@@ -132,28 +135,29 @@ suite('viewLineRenderer.renderLine', () => {
[0],
[0],
[0],
- [1],
+ [0, 1],
]);
});
test('typical line', () => {
let lineText = '\t export class Game { // http://test.com ';
let lineParts = [
- createPart(0, 'block meta ts vs-whitespace'),
- createPart(5, 'block declaration meta modifier object storage ts'),
- createPart(11, 'block declaration meta object ts'),
- createPart(12, 'block declaration meta object storage type ts'),
- createPart(17, 'block declaration meta object ts'),
- createPart(18, 'block class declaration entity meta name object ts'),
- createPart(22, 'block declaration meta object ts'),
- createPart(23, 'delimiter curly typescript'),
- createPart(24, 'block body declaration meta object ts'),
- createPart(25, 'block body comment declaration line meta object ts'),
- createPart(28, 'block body comment declaration line meta object ts detected-link'),
- createPart(43, 'block body comment declaration line meta object ts vs-whitespace'),
+ createPart(5, 'block meta ts'),
+ createPart(11, 'block declaration meta modifier object storage ts'),
+ createPart(12, 'block declaration meta object ts'),
+ createPart(17, 'block declaration meta object storage type ts'),
+ createPart(18, 'block declaration meta object ts'),
+ createPart(22, 'block class declaration entity meta name object ts'),
+ createPart(23, 'block declaration meta object ts'),
+ createPart(24, 'delimiter curly typescript'),
+ createPart(25, 'block body declaration meta object ts'),
+ createPart(28, 'block body comment declaration line meta object ts'),
+ createPart(43, 'block body comment declaration line meta object ts detected-link'),
+ createPart(48, 'block body comment declaration line meta object ts'),
];
let expectedOutput = [
- '→ ····',
+ '→ ',
+ '····',
'export',
' ',
'class',
@@ -164,10 +168,12 @@ suite('viewLineRenderer.renderLine', () => {
' ',
'// ',
'http://test.com',
- '·····'
+ '··',
+ '···'
].join('');
let expectedOffsetsArr = [
- [0, 4, 5, 6, 7],
+ [0],
+ [0, 1, 2, 3],
[0, 1, 2, 3, 4, 5],
[0],
[0, 1, 2, 3, 4],
@@ -178,17 +184,20 @@ suite('viewLineRenderer.renderLine', () => {
[0],
[0, 1, 2],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
- [0, 1, 2, 3, 4, 5],
+ [0, 1],
+ [0, 1, 2, 3],
];
- let _actual = renderLine(new RenderLineInput(
+ let _actual = renderViewLine(new RenderLineInput(
lineText,
+ 0,
+ lineParts,
+ [],
4,
10,
-1,
'boundary',
- false,
- new LineParts(lineParts, lineText.length + 1)
+ false
));
assert.equal(_actual.output, '' + expectedOutput + '');
@@ -199,16 +208,16 @@ suite('viewLineRenderer.renderLine', () => {
let lineText = '\t\t\tcursorStyle:\t\t\t\t\t\t(prevOpts.cursorStyle !== newOpts.cursorStyle),';
let lineParts = [
- createPart(0, 'block body decl declaration meta method object ts'), // 3 chars
- createPart(3, 'block body decl declaration member meta method object ts'), // 12 chars
- createPart(15, 'block body decl declaration member meta method object ts'), // 6 chars
- createPart(21, 'delimiter paren typescript'), // 1 char
- createPart(22, 'block body decl declaration member meta method object ts'), // 21 chars
- createPart(43, 'block body comparison decl declaration keyword member meta method object operator ts'), // 2 chars
- createPart(45, 'block body comparison decl declaration keyword member meta method object operator ts'), // 1 char
- createPart(46, 'block body decl declaration member meta method object ts'), // 20 chars
- createPart(66, 'delimiter paren typescript'), // 1 char
- createPart(67, 'block body decl declaration meta method object ts'), // 2 chars
+ createPart(3, 'block body decl declaration meta method object ts'), // 3 chars
+ createPart(15, 'block body decl declaration member meta method object ts'), // 12 chars
+ createPart(21, 'block body decl declaration member meta method object ts'), // 6 chars
+ createPart(22, 'delimiter paren typescript'), // 1 char
+ createPart(43, 'block body decl declaration member meta method object ts'), // 21 chars
+ createPart(45, 'block body comparison decl declaration keyword member meta method object operator ts'), // 2 chars
+ createPart(46, 'block body comparison decl declaration keyword member meta method object operator ts'), // 1 char
+ createPart(66, 'block body decl declaration member meta method object ts'), // 20 chars
+ createPart(67, 'delimiter paren typescript'), // 1 char
+ createPart(68, 'block body decl declaration meta method object ts'), // 2 chars
];
let expectedOutput = [
' ',
@@ -235,14 +244,16 @@ suite('viewLineRenderer.renderLine', () => {
[0, 1] // 2 chars
];
- let _actual = renderLine(new RenderLineInput(
+ let _actual = renderViewLine(new RenderLineInput(
lineText,
+ 0,
+ lineParts,
+ [],
4,
10,
-1,
'none',
- false,
- new LineParts(lineParts, lineText.length + 1)
+ false
));
assert.equal(_actual.output, '' + expectedOutput + '');
@@ -253,16 +264,16 @@ suite('viewLineRenderer.renderLine', () => {
let lineText = ' \t\t\tcursorStyle:\t\t\t\t\t\t(prevOpts.cursorStyle !== newOpts.cursorStyle),';
let lineParts = [
- createPart(0, 'block body decl declaration meta method object ts'), // 4 chars
- createPart(4, 'block body decl declaration member meta method object ts'), // 12 chars
- createPart(16, 'block body decl declaration member meta method object ts'), // 6 chars
- createPart(22, 'delimiter paren typescript'), // 1 char
- createPart(23, 'block body decl declaration member meta method object ts'), // 21 chars
- createPart(44, 'block body comparison decl declaration keyword member meta method object operator ts'), // 2 chars
- createPart(46, 'block body comparison decl declaration keyword member meta method object operator ts'), // 1 char
- createPart(47, 'block body decl declaration member meta method object ts'), // 20 chars
- createPart(67, 'delimiter paren typescript'), // 1 char
- createPart(68, 'block body decl declaration meta method object ts'), // 2 chars
+ createPart(4, 'block body decl declaration meta method object ts'), // 4 chars
+ createPart(16, 'block body decl declaration member meta method object ts'), // 12 chars
+ createPart(22, 'block body decl declaration member meta method object ts'), // 6 chars
+ createPart(23, 'delimiter paren typescript'), // 1 char
+ createPart(44, 'block body decl declaration member meta method object ts'), // 21 chars
+ createPart(46, 'block body comparison decl declaration keyword member meta method object operator ts'), // 2 chars
+ createPart(47, 'block body comparison decl declaration keyword member meta method object operator ts'), // 1 char
+ createPart(67, 'block body decl declaration member meta method object ts'), // 20 chars
+ createPart(68, 'delimiter paren typescript'), // 1 char
+ createPart(69, 'block body decl declaration meta method object ts'), // 2 chars
];
let expectedOutput = [
' ',
@@ -289,14 +300,16 @@ suite('viewLineRenderer.renderLine', () => {
[0, 1] // 2 chars
];
- let _actual = renderLine(new RenderLineInput(
+ let _actual = renderViewLine(new RenderLineInput(
lineText,
+ 0,
+ lineParts,
+ [],
4,
10,
-1,
'none',
- false,
- new LineParts(lineParts, lineText.length + 1)
+ false
));
assert.equal(_actual.output, '' + expectedOutput + '');
diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts
index b236973e1a6..92a8a5dc3c7 100644
--- a/src/vs/monaco.d.ts
+++ b/src/vs/monaco.d.ts
@@ -1154,10 +1154,9 @@ declare module monaco.editor {
*/
roundedSelection?: boolean;
/**
- * Theme to be used for rendering. Consists of two parts, the UI theme and the syntax theme,
- * separated by a space.
- * The current available UI themes are: 'vs' (default), 'vs-dark', 'hc-black'
- * The syntax themes are contributed. The default is 'default-theme'
+ * Theme to be used for rendering.
+ * The current out-of-the-box available themes are: 'vs' (default), 'vs-dark', 'hc-black'.
+ * You can create custom themes via `monaco.editor.defineTheme`.
*/
theme?: string;
/**
@@ -1613,6 +1612,10 @@ declare module monaco.editor {
* CSS class name describing the decoration.
*/
className?: string;
+ /**
+ * Message to be rendered when hovering over the glyph margin decoration.
+ */
+ glyphMarginHoverMessage?: MarkedString | MarkedString[];
/**
* Array of MarkedString to render as the decoration message.
*/
diff --git a/src/vs/platform/actions/common/menu.ts b/src/vs/platform/actions/common/menu.ts
new file mode 100644
index 00000000000..621eee3a73a
--- /dev/null
+++ b/src/vs/platform/actions/common/menu.ts
@@ -0,0 +1,139 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 Event, { Emitter } from 'vs/base/common/event';
+import { IDisposable, dispose } from 'vs/base/common/lifecycle';
+import { TPromise } from 'vs/base/common/winjs.base';
+import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
+import { MenuId, MenuRegistry, MenuItemAction, IMenu, IMenuItem } from 'vs/platform/actions/common/actions';
+import { ICommandService } from 'vs/platform/commands/common/commands';
+
+type MenuItemGroup = [string, IMenuItem[]];
+
+export class Menu implements IMenu {
+
+ private _menuGroups: MenuItemGroup[] = [];
+ private _disposables: IDisposable[] = [];
+ private _onDidChange = new Emitter();
+
+ constructor(
+ id: MenuId,
+ startupSignal: TPromise,
+ @ICommandService private _commandService: ICommandService,
+ @IContextKeyService private _contextKeyService: IContextKeyService
+ ) {
+ startupSignal.then(_ => {
+ const menuItems = MenuRegistry.getMenuItems(id);
+ const keysFilter = new Set();
+
+ let group: MenuItemGroup;
+ menuItems.sort(Menu._compareMenuItems);
+
+ for (let item of menuItems) {
+ // group by groupId
+ const groupName = item.group;
+ if (!group || group[0] !== groupName) {
+ group = [groupName, []];
+ this._menuGroups.push(group);
+ }
+ group[1].push(item);
+
+ // keep keys for eventing
+ Menu._fillInKbExprKeys(item.when, keysFilter);
+ }
+
+ // subscribe to context changes
+ this._disposables.push(this._contextKeyService.onDidChangeContext(keys => {
+ for (let k of keys) {
+ if (keysFilter.has(k)) {
+ this._onDidChange.fire();
+ return;
+ }
+ }
+ }));
+
+ this._onDidChange.fire(this);
+ });
+ }
+
+ dispose() {
+ this._disposables = dispose(this._disposables);
+ this._onDidChange.dispose();
+ }
+
+ get onDidChange(): Event {
+ return this._onDidChange.event;
+ }
+
+ getActions(arg?: any): [string, MenuItemAction[]][] {
+ const result: [string, MenuItemAction[]][] = [];
+ for (let group of this._menuGroups) {
+ const [id, items] = group;
+ const activeActions: MenuItemAction[] = [];
+ for (const item of items) {
+ if (this._contextKeyService.contextMatchesRules(item.when)) {
+ const action = new MenuItemAction(item.command, item.alt, arg, this._commandService);
+ action.order = item.order; //TODO@Ben order is menu item property, not an action property
+ activeActions.push(action);
+ }
+ }
+ if (activeActions.length > 0) {
+ result.push([id, activeActions]);
+ }
+ }
+ return result;
+ }
+
+ private static _fillInKbExprKeys(exp: ContextKeyExpr, set: Set): void {
+ if (exp) {
+ for (let key of exp.keys()) {
+ set.add(key);
+ }
+ }
+ }
+
+ private static _compareMenuItems(a: IMenuItem, b: IMenuItem): number {
+
+ let aGroup = a.group;
+ let bGroup = b.group;
+
+ if (aGroup !== bGroup) {
+
+ // Falsy groups come last
+ if (!aGroup) {
+ return 1;
+ } else if (!bGroup) {
+ return -1;
+ }
+
+ // 'navigation' group comes first
+ if (aGroup === 'navigation') {
+ return -1;
+ } else if (bGroup === 'navigation') {
+ return 1;
+ }
+
+ // lexical sort for groups
+ let value = aGroup.localeCompare(bGroup);
+ if (value !== 0) {
+ return value;
+ }
+ }
+
+ // sort on priority - default is 0
+ let aPrio = a.order || 0;
+ let bPrio = b.order || 0;
+ if (aPrio < bPrio) {
+ return -1;
+ } else if (aPrio > bPrio) {
+ return 1;
+ }
+
+ // sort on titles
+ return a.command.title.localeCompare(b.command.title);
+ }
+}
diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts
index 729428af7ba..87d79822189 100644
--- a/src/vs/platform/actions/common/menuService.ts
+++ b/src/vs/platform/actions/common/menuService.ts
@@ -5,11 +5,10 @@
'use strict';
-import Event, { Emitter } from 'vs/base/common/event';
-import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/collections';
-import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
-import { MenuId, MenuRegistry, ICommandAction, MenuItemAction, IMenu, IMenuItem, IMenuService } from 'vs/platform/actions/common/actions';
+import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
+import { MenuId, MenuRegistry, ICommandAction, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
+import { Menu } from 'vs/platform/actions/common/menu';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -25,137 +24,10 @@ export class MenuService implements IMenuService {
}
createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu {
- return new Menu(id, this._commandService, contextKeyService, this._extensionService);
+ return new Menu(id, this._extensionService.onReady(), this._commandService, contextKeyService);
}
getCommandActions(): ICommandAction[] {
return values(MenuRegistry.commands);
}
}
-
-type MenuItemGroup = [string, IMenuItem[]];
-
-class Menu implements IMenu {
-
- private _menuGroups: MenuItemGroup[] = [];
- private _disposables: IDisposable[] = [];
- private _onDidChange = new Emitter();
-
- constructor(
- id: MenuId,
- @ICommandService private _commandService: ICommandService,
- @IContextKeyService private _contextKeyService: IContextKeyService,
- @IExtensionService private _extensionService: IExtensionService
- ) {
- this._extensionService.onReady().then(_ => {
-
- const menuItems = MenuRegistry.getMenuItems(id);
- const keysFilter = new Set();
-
- let group: MenuItemGroup;
- menuItems.sort(Menu._compareMenuItems);
-
- for (let item of menuItems) {
- // group by groupId
- const groupName = item.group;
- if (!group || group[0] !== groupName) {
- group = [groupName, []];
- this._menuGroups.push(group);
- }
- group[1].push(item);
-
- // keep keys for eventing
- Menu._fillInKbExprKeys(item.when, keysFilter);
- }
-
- // subscribe to context changes
- this._disposables.push(this._contextKeyService.onDidChangeContext(keys => {
- for (let k of keys) {
- if (keysFilter.has(k)) {
- this._onDidChange.fire();
- return;
- }
- }
- }));
-
- this._onDidChange.fire(this);
- });
- }
-
- dispose() {
- this._disposables = dispose(this._disposables);
- this._onDidChange.dispose();
- }
-
- get onDidChange(): Event {
- return this._onDidChange.event;
- }
-
- getActions(arg?: any): [string, MenuItemAction[]][] {
- const result: [string, MenuItemAction[]][] = [];
- for (let group of this._menuGroups) {
- const [id, items] = group;
- const activeActions: MenuItemAction[] = [];
- for (const item of items) {
- if (this._contextKeyService.contextMatchesRules(item.when)) {
- const action = new MenuItemAction(item.command, item.alt, arg, this._commandService);
- action.order = item.order; //TODO@Ben order is menu item property, not an action property
- activeActions.push(action);
- }
- }
- if (activeActions.length > 0) {
- result.push([id, activeActions]);
- }
- }
- return result;
- }
-
- private static _fillInKbExprKeys(exp: ContextKeyExpr, set: Set): void {
- if (exp) {
- for (let key of exp.keys()) {
- set.add(key);
- }
- }
- }
-
- private static _compareMenuItems(a: IMenuItem, b: IMenuItem): number {
-
- let aGroup = a.group;
- let bGroup = b.group;
-
- if (aGroup !== bGroup) {
-
- // Falsy groups come last
- if (!aGroup) {
- return 1;
- } else if (!bGroup) {
- return -1;
- }
-
- // 'navigation' group comes first
- if (aGroup === 'navigation') {
- return -1;
- } else if (bGroup === 'navigation') {
- return 1;
- }
-
- // lexical sort for groups
- let value = aGroup.localeCompare(bGroup);
- if (value !== 0) {
- return value;
- }
- }
-
- // sort on priority - default is 0
- let aPrio = a.order || 0;
- let bPrio = b.order || 0;
- if (aPrio < bPrio) {
- return -1;
- } else if (aPrio > bPrio) {
- return 1;
- }
-
- // sort on titles
- return a.command.title.localeCompare(b.command.title);
- }
-}
diff --git a/src/vs/platform/extensions/common/abstractExtensionService.ts b/src/vs/platform/extensions/common/abstractExtensionService.ts
index 47ca864e829..25ae438be94 100644
--- a/src/vs/platform/extensions/common/abstractExtensionService.ts
+++ b/src/vs/platform/extensions/common/abstractExtensionService.ts
@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { IExtensionDescription, IExtensionService, IExtensionsStatus, ExtensionPointContribution } from 'vs/platform/extensions/common/extensions';
-import { IExtensionPoint, onWillActivate } from 'vs/platform/extensions/common/extensionsRegistry';
+import { IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
const hasOwnProperty = Object.hasOwnProperty;
@@ -105,7 +105,6 @@ export abstract class AbstractExtensionService imp
}
private _activateByEvent(activationEvent: string): TPromise {
- onWillActivate.fire(activationEvent);
let activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent);
return this._activateExtensions(activateExtensions, 0);
}
diff --git a/src/vs/platform/extensions/common/extensionsRegistry.ts b/src/vs/platform/extensions/common/extensionsRegistry.ts
index 4cb3ac5b266..101e8de6830 100644
--- a/src/vs/platform/extensions/common/extensionsRegistry.ts
+++ b/src/vs/platform/extensions/common/extensionsRegistry.ts
@@ -11,13 +11,10 @@ import Severity from 'vs/base/common/severity';
import { IMessage, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { Registry } from 'vs/platform/platform';
-import { Emitter } from 'vs/base/common/event';
const hasOwnProperty = Object.hasOwnProperty;
const schemaRegistry = Registry.as(Extensions.JSONContribution);
-export const onWillActivate: Emitter = new Emitter();
-
export class ExtensionMessageCollector {
private _messageHandler: (msg: IMessage) => void;
diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts
index 798e9727062..dd6343e6897 100644
--- a/src/vs/platform/telemetry/common/telemetry.ts
+++ b/src/vs/platform/telemetry/common/telemetry.ts
@@ -5,16 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
-import { IDisposable } from 'vs/base/common/lifecycle';
-import { guessMimeTypes } from 'vs/base/common/mime';
-import paths = require('vs/base/common/paths');
-import URI from 'vs/base/common/uri';
-import { ConfigurationSource, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
-import { IKeybindingService, KeybindingSource } from 'vs/platform/keybinding/common/keybinding';
-import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
-import { IStorageService } from 'vs/platform/storage/common/storage';
-import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
export const ITelemetryService = createDecorator('telemetryService');
@@ -46,268 +37,3 @@ export interface ITelemetryService {
getExperiments(): ITelemetryExperiments;
}
-
-export const defaultExperiments: ITelemetryExperiments = {
- showNewUserWatermark: false,
- openUntitledFile: true
-};
-
-export const NullTelemetryService = {
- _serviceBrand: undefined,
- _experiments: defaultExperiments,
- publicLog(eventName: string, data?: any) {
- return TPromise.as(null);
- },
- isOptedIn: true,
- getTelemetryInfo(): TPromise {
- return TPromise.as({
- instanceId: 'someValue.instanceId',
- sessionId: 'someValue.sessionId',
- machineId: 'someValue.machineId'
- });
- },
- getExperiments(): ITelemetryExperiments {
- return this._experiments;
- }
-};
-
-const beginGettingStartedExp = Date.UTC(2017, 0, 9);
-const endGettingStartedExp = Date.UTC(2017, 0, 16);
-
-export function loadExperiments(contextService: IWorkspaceContextService, storageService: IStorageService, configurationService: IConfigurationService): ITelemetryExperiments {
-
- const key = 'experiments.randomness';
- let valueString = storageService.get(key);
- if (!valueString) {
- valueString = Math.random().toString();
- storageService.store(key, valueString);
- }
-
- const random1 = parseFloat(valueString);
- let [random2, showNewUserWatermark] = splitRandom(random1);
- let [random3, openUntitledFile] = splitRandom(random2);
- let [, openGettingStarted] = splitRandom(random3);
-
- const newUserDuration = 24 * 60 * 60 * 1000;
- const firstSessionDate = storageService.get('telemetry.firstSessionDate');
- const isNewUser = !firstSessionDate || Date.now() - Date.parse(firstSessionDate) < newUserDuration;
- if (!isNewUser || contextService.hasWorkspace()) {
- showNewUserWatermark = defaultExperiments.showNewUserWatermark;
- openUntitledFile = defaultExperiments.openUntitledFile;
- }
-
- const isNewSession = !storageService.get('telemetry.lastSessionDate');
- const now = Date.now();
- if (!(isNewSession && now >= beginGettingStartedExp && now < endGettingStartedExp)) {
- openGettingStarted = undefined;
- }
-
- return applyOverrides(configurationService, {
- showNewUserWatermark,
- openUntitledFile,
- openGettingStarted
- });
-}
-
-export function applyOverrides(configurationService: IConfigurationService, experiments: ITelemetryExperiments): ITelemetryExperiments {
- const config: any = configurationService.getConfiguration('telemetry');
- const experimentsConfig = config && config.experiments || {};
- Object.keys(experiments).forEach(key => {
- if (key in experimentsConfig) {
- experiments[key] = experimentsConfig[key];
- }
- });
- return experiments;
-}
-
-function splitRandom(random: number): [number, boolean] {
- const scaled = random * 2;
- const i = Math.floor(scaled);
- return [scaled - i, i === 1];
-}
-
-export interface ITelemetryAppender {
- log(eventName: string, data: any): void;
-}
-
-export function combinedAppender(...appenders: ITelemetryAppender[]): ITelemetryAppender {
- return { log: (e, d) => appenders.forEach(a => a.log(e, d)) };
-}
-
-export const NullAppender: ITelemetryAppender = { log: () => null };
-
-// --- util
-
-export function anonymize(input: string): string {
- if (!input) {
- return input;
- }
-
- let r = '';
- for (let i = 0; i < input.length; i++) {
- let ch = input[i];
- if (ch >= '0' && ch <= '9') {
- r += '0';
- continue;
- }
- if (ch >= 'a' && ch <= 'z') {
- r += 'a';
- continue;
- }
- if (ch >= 'A' && ch <= 'Z') {
- r += 'A';
- continue;
- }
- r += ch;
- }
- return r;
-}
-
-export interface URIDescriptor {
- mimeType?: string;
- ext?: string;
- path?: string;
-}
-
-export function telemetryURIDescriptor(uri: URI): URIDescriptor {
- const fsPath = uri && uri.fsPath;
- return fsPath ? { mimeType: guessMimeTypes(fsPath).join(', '), ext: paths.extname(fsPath), path: anonymize(fsPath) } : {};
-}
-
-const configurationValueWhitelist = [
- 'window.zoomLevel',
- 'editor.fontSize',
- 'editor.fontFamily',
- 'editor.tabSize',
- 'files.autoSave',
- 'files.hotExit',
- 'typescript.check.tscVersion',
- 'editor.renderWhitespace',
- 'editor.cursorBlinking',
- 'editor.cursorStyle',
- 'files.associations',
- 'workbench.statusBar.visible',
- 'editor.wrappingColumn',
- 'editor.insertSpaces',
- 'editor.renderIndentGuides',
- 'files.trimTrailingWhitespace',
- 'git.confirmSync',
- 'editor.rulers',
- 'workbench.sideBar.location',
- 'editor.fontLigatures',
- 'editor.wordWrap',
- 'editor.lineHeight',
- 'editor.detectIndentation',
- 'editor.formatOnType',
- 'editor.formatOnSave',
- 'window.openFilesInNewWindow',
- 'javascript.validate.enable',
- 'editor.mouseWheelZoom',
- 'typescript.check.workspaceVersion',
- 'editor.fontWeight',
- 'editor.scrollBeyondLastLine',
- 'editor.lineNumbers',
- 'editor.wrappingIndent',
- 'editor.renderControlCharacters',
- 'editor.autoClosingBrackets',
- 'window.reopenFolders',
- 'extensions.autoUpdate',
- 'editor.tabCompletion',
- 'files.eol',
- 'explorer.openEditors.visible',
- 'workbench.editor.enablePreview',
- 'files.autoSaveDelay',
- 'editor.roundedSelection',
- 'editor.quickSuggestions',
- 'editor.acceptSuggestionOnEnter',
- 'workbench.editor.showTabs',
- 'files.encoding',
- 'editor.quickSuggestionsDelay',
- 'editor.snippetSuggestions',
- 'editor.selectionHighlight',
- 'editor.glyphMargin',
- 'php.validate.run',
- 'editor.wordSeparators',
- 'editor.mouseWheelScrollSensitivity',
- 'editor.suggestOnTriggerCharacters',
- 'git.enabled',
- 'http.proxyStrictSSL',
- 'terminal.integrated.fontFamily',
- 'editor.overviewRulerLanes',
- 'editor.wordBasedSuggestions',
- 'editor.hideCursorInOverviewRuler',
- 'editor.trimAutoWhitespace',
- 'editor.folding',
- 'workbench.editor.enablePreviewFromQuickOpen',
- 'php.validate.enable',
- 'editor.parameterHints',
-];
-
-export function configurationTelemetry(telemetryService: ITelemetryService, configurationService: IConfigurationService): IDisposable {
- return configurationService.onDidUpdateConfiguration(event => {
- if (event.source !== ConfigurationSource.Default) {
- telemetryService.publicLog('updateConfiguration', {
- configurationSource: ConfigurationSource[event.source],
- configurationKeys: flattenKeys(event.sourceConfig)
- });
- telemetryService.publicLog('updateConfigurationValues', {
- configurationSource: ConfigurationSource[event.source],
- configurationValues: flattenValues(event.sourceConfig, configurationValueWhitelist)
- });
- }
- });
-}
-
-export function lifecycleTelemetry(telemetryService: ITelemetryService, lifecycleService: ILifecycleService): IDisposable {
- return lifecycleService.onShutdown(event => {
- telemetryService.publicLog('shutdown', { reason: ShutdownReason[event] });
- });
-}
-
-export function keybindingsTelemetry(telemetryService: ITelemetryService, keybindingService: IKeybindingService): IDisposable {
- return keybindingService.onDidUpdateKeybindings(event => {
- if (event.source === KeybindingSource.User && event.keybindings) {
- telemetryService.publicLog('updateKeybindings', {
- bindings: event.keybindings.map(binding => ({
- key: binding.key,
- command: binding.command,
- when: binding.when,
- args: binding.args ? true : undefined
- }))
- });
- }
- });
-}
-
-function flattenKeys(value: Object): string[] {
- if (!value) {
- return [];
- }
- const result: string[] = [];
- flatKeys(result, '', value);
- return result;
-}
-
-function flatKeys(result: string[], prefix: string, value: Object): void {
- if (value && typeof value === 'object' && !Array.isArray(value)) {
- Object.keys(value)
- .forEach(key => flatKeys(result, prefix ? `${prefix}.${key}` : key, value[key]));
- } else {
- result.push(prefix);
- }
-}
-
-function flattenValues(value: Object, keys: string[]): { [key: string]: any }[] {
- if (!value) {
- return [];
- }
-
- return keys.reduce((array, key) => {
- const v = key.split('.')
- .reduce((tmp, k) => tmp && typeof tmp === 'object' ? tmp[k] : undefined, value);
- if (typeof v !== 'undefined') {
- array.push({ [key]: v });
- }
- return array;
- }, []);
-}
\ No newline at end of file
diff --git a/src/vs/platform/telemetry/common/telemetryIpc.ts b/src/vs/platform/telemetry/common/telemetryIpc.ts
index 7cf10b99e70..90aa9ac4f5d 100644
--- a/src/vs/platform/telemetry/common/telemetryIpc.ts
+++ b/src/vs/platform/telemetry/common/telemetryIpc.ts
@@ -7,7 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
-import { ITelemetryAppender } from './telemetry';
+import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
export interface ITelemetryLog {
eventName: string;
diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts
index 36007755990..40937bc571c 100644
--- a/src/vs/platform/telemetry/common/telemetryService.ts
+++ b/src/vs/platform/telemetry/common/telemetryService.ts
@@ -7,7 +7,8 @@
import { localize } from 'vs/nls';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
-import { ITelemetryService, ITelemetryAppender, ITelemetryInfo, ITelemetryExperiments, defaultExperiments } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryAppender, defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts
new file mode 100644
index 00000000000..fbc9f69d620
--- /dev/null
+++ b/src/vs/platform/telemetry/common/telemetryUtils.ts
@@ -0,0 +1,282 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 { TPromise } from 'vs/base/common/winjs.base';
+import { IDisposable } from 'vs/base/common/lifecycle';
+import { guessMimeTypes } from 'vs/base/common/mime';
+import paths = require('vs/base/common/paths');
+import URI from 'vs/base/common/uri';
+import { ConfigurationSource, IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { IKeybindingService, KeybindingSource } from 'vs/platform/keybinding/common/keybinding';
+import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
+import { IStorageService } from 'vs/platform/storage/common/storage';
+import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
+import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
+
+export const defaultExperiments: ITelemetryExperiments = {
+ showNewUserWatermark: false,
+ openUntitledFile: true
+};
+
+export const NullTelemetryService = {
+ _serviceBrand: undefined,
+ _experiments: defaultExperiments,
+ publicLog(eventName: string, data?: any) {
+ return TPromise.as(null);
+ },
+ isOptedIn: true,
+ getTelemetryInfo(): TPromise {
+ return TPromise.as({
+ instanceId: 'someValue.instanceId',
+ sessionId: 'someValue.sessionId',
+ machineId: 'someValue.machineId'
+ });
+ },
+ getExperiments(): ITelemetryExperiments {
+ return this._experiments;
+ }
+};
+
+const beginGettingStartedExp = Date.UTC(2017, 0, 9);
+const endGettingStartedExp = Date.UTC(2017, 0, 16);
+
+export function loadExperiments(contextService: IWorkspaceContextService, storageService: IStorageService, configurationService: IConfigurationService): ITelemetryExperiments {
+
+ const key = 'experiments.randomness';
+ let valueString = storageService.get(key);
+ if (!valueString) {
+ valueString = Math.random().toString();
+ storageService.store(key, valueString);
+ }
+
+ const random1 = parseFloat(valueString);
+ let [random2, showNewUserWatermark] = splitRandom(random1);
+ let [random3, openUntitledFile] = splitRandom(random2);
+ let [, openGettingStarted] = splitRandom(random3);
+
+ const newUserDuration = 24 * 60 * 60 * 1000;
+ const firstSessionDate = storageService.get('telemetry.firstSessionDate');
+ const isNewUser = !firstSessionDate || Date.now() - Date.parse(firstSessionDate) < newUserDuration;
+ if (!isNewUser || contextService.hasWorkspace()) {
+ showNewUserWatermark = defaultExperiments.showNewUserWatermark;
+ openUntitledFile = defaultExperiments.openUntitledFile;
+ }
+
+ const isNewSession = !storageService.get('telemetry.lastSessionDate');
+ const now = Date.now();
+ if (!(isNewSession && now >= beginGettingStartedExp && now < endGettingStartedExp)) {
+ openGettingStarted = undefined;
+ }
+
+ return applyOverrides(configurationService, {
+ showNewUserWatermark,
+ openUntitledFile,
+ openGettingStarted
+ });
+}
+
+export function applyOverrides(configurationService: IConfigurationService, experiments: ITelemetryExperiments): ITelemetryExperiments {
+ const config: any = configurationService.getConfiguration('telemetry');
+ const experimentsConfig = config && config.experiments || {};
+ Object.keys(experiments).forEach(key => {
+ if (key in experimentsConfig) {
+ experiments[key] = experimentsConfig[key];
+ }
+ });
+ return experiments;
+}
+
+function splitRandom(random: number): [number, boolean] {
+ const scaled = random * 2;
+ const i = Math.floor(scaled);
+ return [scaled - i, i === 1];
+}
+
+export interface ITelemetryAppender {
+ log(eventName: string, data: any): void;
+}
+
+export function combinedAppender(...appenders: ITelemetryAppender[]): ITelemetryAppender {
+ return { log: (e, d) => appenders.forEach(a => a.log(e, d)) };
+}
+
+export const NullAppender: ITelemetryAppender = { log: () => null };
+
+// --- util
+
+export function anonymize(input: string): string {
+ if (!input) {
+ return input;
+ }
+
+ let r = '';
+ for (let i = 0; i < input.length; i++) {
+ let ch = input[i];
+ if (ch >= '0' && ch <= '9') {
+ r += '0';
+ continue;
+ }
+ if (ch >= 'a' && ch <= 'z') {
+ r += 'a';
+ continue;
+ }
+ if (ch >= 'A' && ch <= 'Z') {
+ r += 'A';
+ continue;
+ }
+ r += ch;
+ }
+ return r;
+}
+
+export interface URIDescriptor {
+ mimeType?: string;
+ ext?: string;
+ path?: string;
+}
+
+export function telemetryURIDescriptor(uri: URI): URIDescriptor {
+ const fsPath = uri && uri.fsPath;
+ return fsPath ? { mimeType: guessMimeTypes(fsPath).join(', '), ext: paths.extname(fsPath), path: anonymize(fsPath) } : {};
+}
+
+const configurationValueWhitelist = [
+ 'window.zoomLevel',
+ 'editor.fontSize',
+ 'editor.fontFamily',
+ 'editor.tabSize',
+ 'files.autoSave',
+ 'files.hotExit',
+ 'typescript.check.tscVersion',
+ 'editor.renderWhitespace',
+ 'editor.cursorBlinking',
+ 'editor.cursorStyle',
+ 'files.associations',
+ 'workbench.statusBar.visible',
+ 'editor.wrappingColumn',
+ 'editor.insertSpaces',
+ 'editor.renderIndentGuides',
+ 'files.trimTrailingWhitespace',
+ 'git.confirmSync',
+ 'editor.rulers',
+ 'workbench.sideBar.location',
+ 'editor.fontLigatures',
+ 'editor.wordWrap',
+ 'editor.lineHeight',
+ 'editor.detectIndentation',
+ 'editor.formatOnType',
+ 'editor.formatOnSave',
+ 'window.openFilesInNewWindow',
+ 'javascript.validate.enable',
+ 'editor.mouseWheelZoom',
+ 'typescript.check.workspaceVersion',
+ 'editor.fontWeight',
+ 'editor.scrollBeyondLastLine',
+ 'editor.lineNumbers',
+ 'editor.wrappingIndent',
+ 'editor.renderControlCharacters',
+ 'editor.autoClosingBrackets',
+ 'window.reopenFolders',
+ 'extensions.autoUpdate',
+ 'editor.tabCompletion',
+ 'files.eol',
+ 'explorer.openEditors.visible',
+ 'workbench.editor.enablePreview',
+ 'files.autoSaveDelay',
+ 'editor.roundedSelection',
+ 'editor.quickSuggestions',
+ 'editor.acceptSuggestionOnEnter',
+ 'workbench.editor.showTabs',
+ 'files.encoding',
+ 'editor.quickSuggestionsDelay',
+ 'editor.snippetSuggestions',
+ 'editor.selectionHighlight',
+ 'editor.glyphMargin',
+ 'php.validate.run',
+ 'editor.wordSeparators',
+ 'editor.mouseWheelScrollSensitivity',
+ 'editor.suggestOnTriggerCharacters',
+ 'git.enabled',
+ 'http.proxyStrictSSL',
+ 'terminal.integrated.fontFamily',
+ 'editor.overviewRulerLanes',
+ 'editor.wordBasedSuggestions',
+ 'editor.hideCursorInOverviewRuler',
+ 'editor.trimAutoWhitespace',
+ 'editor.folding',
+ 'workbench.editor.enablePreviewFromQuickOpen',
+ 'php.validate.enable',
+ 'editor.parameterHints',
+];
+
+export function configurationTelemetry(telemetryService: ITelemetryService, configurationService: IConfigurationService): IDisposable {
+ return configurationService.onDidUpdateConfiguration(event => {
+ if (event.source !== ConfigurationSource.Default) {
+ telemetryService.publicLog('updateConfiguration', {
+ configurationSource: ConfigurationSource[event.source],
+ configurationKeys: flattenKeys(event.sourceConfig)
+ });
+ telemetryService.publicLog('updateConfigurationValues', {
+ configurationSource: ConfigurationSource[event.source],
+ configurationValues: flattenValues(event.sourceConfig, configurationValueWhitelist)
+ });
+ }
+ });
+}
+
+export function lifecycleTelemetry(telemetryService: ITelemetryService, lifecycleService: ILifecycleService): IDisposable {
+ return lifecycleService.onShutdown(event => {
+ telemetryService.publicLog('shutdown', { reason: ShutdownReason[event] });
+ });
+}
+
+export function keybindingsTelemetry(telemetryService: ITelemetryService, keybindingService: IKeybindingService): IDisposable {
+ return keybindingService.onDidUpdateKeybindings(event => {
+ if (event.source === KeybindingSource.User && event.keybindings) {
+ telemetryService.publicLog('updateKeybindings', {
+ bindings: event.keybindings.map(binding => ({
+ key: binding.key,
+ command: binding.command,
+ when: binding.when,
+ args: binding.args ? true : undefined
+ }))
+ });
+ }
+ });
+}
+
+function flattenKeys(value: Object): string[] {
+ if (!value) {
+ return [];
+ }
+ const result: string[] = [];
+ flatKeys(result, '', value);
+ return result;
+}
+
+function flatKeys(result: string[], prefix: string, value: Object): void {
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
+ Object.keys(value)
+ .forEach(key => flatKeys(result, prefix ? `${prefix}.${key}` : key, value[key]));
+ } else {
+ result.push(prefix);
+ }
+}
+
+function flattenValues(value: Object, keys: string[]): { [key: string]: any }[] {
+ if (!value) {
+ return [];
+ }
+
+ return keys.reduce((array, key) => {
+ const v = key.split('.')
+ .reduce((tmp, k) => tmp && typeof tmp === 'object' ? tmp[k] : undefined, value);
+ if (typeof v !== 'undefined') {
+ array.push({ [key]: v });
+ }
+ return array;
+ }, []);
+}
diff --git a/src/vs/platform/telemetry/node/appInsightsAppender.ts b/src/vs/platform/telemetry/node/appInsightsAppender.ts
index 50f19c0778a..a6a3322cbb9 100644
--- a/src/vs/platform/telemetry/node/appInsightsAppender.ts
+++ b/src/vs/platform/telemetry/node/appInsightsAppender.ts
@@ -8,7 +8,7 @@ import * as appInsights from 'applicationinsights';
import { isObject } from 'vs/base/common/types';
import { safeStringify, mixin } from 'vs/base/common/objects';
import { TPromise } from 'vs/base/common/winjs.base';
-import { ITelemetryAppender } from '../common/telemetry';
+import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
let _initialized = false;
diff --git a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts
index 6e6bb729c9e..0f6157ba5e6 100644
--- a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts
+++ b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts
@@ -9,14 +9,14 @@ import { Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
-import Telemetry = require('vs/platform/telemetry/common/telemetry');
+import { NullAppender, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import Errors = require('vs/base/common/errors');
import * as sinon from 'sinon';
import { getConfigurationValue } from 'vs/platform/configuration/common/configuration';
const optInStatusEventName: string = 'optInStatus';
-class TestTelemetryAppender implements Telemetry.ITelemetryAppender {
+class TestTelemetryAppender implements ITelemetryAppender {
public events: any[];
public isDisposed: boolean;
@@ -174,7 +174,7 @@ suite('TelemetryService', () => {
test('TelemetryInfo comes from properties', function () {
let service = new TelemetryService({
- appender: Telemetry.NullAppender,
+ appender: NullAppender,
commonProperties: TPromise.as({
sessionID: 'one',
['common.instanceId']: 'two',
diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts
index 82be13ee78d..f415b76bc13 100644
--- a/src/vs/vscode.d.ts
+++ b/src/vs/vscode.d.ts
@@ -1057,28 +1057,28 @@ declare module 'vscode' {
* Scheme is the `http` part of `http://www.msft.com/some/path?query#fragment`.
* The part before the first colon.
*/
- scheme: string;
+ readonly scheme: string;
/**
* Authority is the `www.msft.com` part of `http://www.msft.com/some/path?query#fragment`.
* The part between the first double slashes and the next slash.
*/
- authority: string;
+ readonly authority: string;
/**
* Path is the `/some/path` part of `http://www.msft.com/some/path?query#fragment`.
*/
- path: string;
+ readonly path: string;
/**
* Query is the `query` part of `http://www.msft.com/some/path?query#fragment`.
*/
- query: string;
+ readonly query: string;
/**
* Fragment is the `fragment` part of `http://www.msft.com/some/path?query#fragment`.
*/
- fragment: string;
+ readonly fragment: string;
/**
* The string representing the corresponding file system path of this Uri.
@@ -1087,7 +1087,7 @@ declare module 'vscode' {
* uses the platform specific path separator. Will *not* validate the path for
* invalid characters and semantics. Will *not* look at the scheme of this Uri.
*/
- fsPath: string;
+ readonly fsPath: string;
/**
* Derive a new Uri from this Uri.
@@ -3646,15 +3646,6 @@ declare module 'vscode' {
*/
export function createOutputChannel(name: string): OutputChannel;
- /**
- * Set a message to the status bar. This is a short hand for the more powerful
- * status bar [items](#window.createStatusBarItem).
- *
- * @param text The message to show, support icon subtitution as in status bar [items](#StatusBarItem.text).
- * @return A disposable which hides the status bar message.
- */
- export function setStatusBarMessage(text: string): Disposable;
-
/**
* Set a message to the status bar. This is a short hand for the more powerful
* status bar [items](#window.createStatusBarItem).
@@ -3675,6 +3666,18 @@ declare module 'vscode' {
*/
export function setStatusBarMessage(text: string, hideWhenDone: Thenable): Disposable;
+ /**
+ * Set a message to the status bar. This is a short hand for the more powerful
+ * status bar [items](#window.createStatusBarItem).
+ *
+ * *Note* that status bar messages stack and that they must be disposed when no
+ * longer used.
+ *
+ * @param text The message to show, support icon subtitution as in status bar [items](#StatusBarItem.text).
+ * @return A disposable which hides the status bar message.
+ */
+ export function setStatusBarMessage(text: string): Disposable;
+
/**
* Creates a status bar [item](#StatusBarItem).
*
diff --git a/src/vs/workbench/api/node/mainThreadTerminalService.ts b/src/vs/workbench/api/node/mainThreadTerminalService.ts
index 75baf259baa..83b1f50c893 100644
--- a/src/vs/workbench/api/node/mainThreadTerminalService.ts
+++ b/src/vs/workbench/api/node/mainThreadTerminalService.ts
@@ -5,7 +5,7 @@
'use strict';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
-import { ITerminalService, ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal';
+import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape } from './extHost.protocol';
@@ -31,7 +31,14 @@ export class MainThreadTerminalService extends MainThreadTerminalServiceShape {
}
public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], waitOnExit?: boolean): TPromise {
- return TPromise.as(this.terminalService.createInstance(name, shellPath, shellArgs, waitOnExit, true).id);
+ const shellLaunchConfig: IShellLaunchConfig = {
+ name,
+ executable: shellPath,
+ args: shellArgs,
+ waitOnExit,
+ ignoreConfigurationCwd: true
+ };
+ return TPromise.as(this.terminalService.createInstance(shellLaunchConfig).id);
}
public $show(terminalId: number, preserveFocus: boolean): void {
diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts
index 22d7e8220ce..35120922f07 100644
--- a/src/vs/workbench/browser/parts/editor/editorStatus.ts
+++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts
@@ -27,7 +27,7 @@ import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/
import { IEditorAction, ICommonCodeEditor, IModelContentChangedEvent, IModelOptionsChangedEvent, IModelLanguageChangedEvent, ICursorPositionChangedEvent, EndOfLineSequence, EditorType, IModel, IDiffEditorModel, IEditor } from 'vs/editor/common/editorCommon';
import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/common/linesOperations';
-import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/workbench/parts/indentation/common/indentation';
+import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/workbench/common/editor/indentation';
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import { IEditor as IBaseEditor, IEditorInput } from 'vs/platform/editor/common/editor';
@@ -42,10 +42,9 @@ import { StyleMutator } from 'vs/base/browser/styleMutator';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';
-import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
+import { ICommandService } from 'vs/platform/commands/common/commands';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
-import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
-import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
+import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { getCodeEditor as getEditorWidget } from 'vs/editor/common/services/codeEditorService';
function getCodeEditor(editorWidget: IEditor): ICommonCodeEditor {
@@ -680,24 +679,20 @@ function isWritableCodeEditor(e: IBaseEditor): boolean {
export class ShowLanguageExtensionsAction extends Action {
- static ID = 'workbench.extensions.action.showLanguageExtensions';
+ static ID = 'workbench.action.showLanguageExtensions';
constructor(
- private extension: string,
- @IViewletService private viewletService: IViewletService,
+ private fileExtension: string,
+ @ICommandService private commandService: ICommandService,
@IExtensionGalleryService galleryService: IExtensionGalleryService
) {
- super(ShowLanguageExtensionsAction.ID, nls.localize('showLanguageExtensions', "Search Marketplace Extensions for '{0}'...", extension), null, true);
+ super(ShowLanguageExtensionsAction.ID, nls.localize('showLanguageExtensions', "Search Marketplace Extensions for '{0}'...", fileExtension));
+
this.enabled = galleryService.isEnabled();
}
run(): TPromise {
- return this.viewletService.openViewlet(VIEWLET_ID, true)
- .then(viewlet => viewlet as IExtensionsViewlet)
- .then(viewlet => {
- viewlet.search(`ext:${this.extension.replace(/^\./, '')}`);
- viewlet.focus();
- });
+ return this.commandService.executeCommand('workbench.extensions.action.showLanguageExtensions', this.fileExtension).then(() => void 0);
}
}
diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts
index ac4094b5fe1..d4eb29ba79d 100644
--- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts
+++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts
@@ -12,6 +12,7 @@ import errors = require('vs/base/common/errors');
import DOM = require('vs/base/browser/dom');
import { isMacintosh } from 'vs/base/common/platform';
import { MIME_BINARY } from 'vs/base/common/mime';
+import { shorten } from 'vs/base/common/labels';
import { ActionRunner, IAction } from 'vs/base/common/actions';
import { Position, IEditorInput } from 'vs/platform/editor/common/editor';
import { IEditorGroup, toResource } from 'vs/workbench/common/editor';
@@ -37,7 +38,6 @@ import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElemen
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { extractResources } from 'vs/base/browser/dnd';
import { LinkedMap } from 'vs/base/common/map';
-import paths = require('vs/base/common/paths');
interface IEditorInputLabel {
editor: IEditorInput;
@@ -256,15 +256,10 @@ export class TabsTitleControl extends TitleControl {
const labels: IEditorInputLabel[] = [];
const mapLabelToDuplicates = new LinkedMap();
- const mapLabelAndDescriptionToDuplicates = new LinkedMap();
// Build labels and descriptions for each editor
editors.forEach(editor => {
let description = editor.getDescription();
- if (description && description.indexOf(paths.nativeSep) >= 0) {
- description = paths.basename(description); // optimize for editors that show paths and build a shorter description to keep tab width small
- }
-
const item: IEditorInputLabel = {
editor,
name: editor.getName(),
@@ -274,31 +269,20 @@ export class TabsTitleControl extends TitleControl {
labels.push(item);
mapLabelToDuplicates.getOrSet(item.name, []).push(item);
- if (item.description) {
- mapLabelAndDescriptionToDuplicates.getOrSet(item.name + item.description, []).push(item);
- }
});
- // Mark label duplicates
+ // Mark duplicates and shorten their descriptions
const labelDuplicates = mapLabelToDuplicates.values();
labelDuplicates.forEach(duplicates => {
if (duplicates.length > 1) {
- duplicates.forEach(duplicate => {
+ let shortenedDescriptions = shorten(duplicates.map(duplicate => duplicate.editor.getDescription()));
+ duplicates.forEach((duplicate, i) => {
+ duplicate.description = shortenedDescriptions[i];
duplicate.hasAmbiguousName = true;
});
}
});
- // React to duplicates for combination of label and description
- const descriptionDuplicates = mapLabelAndDescriptionToDuplicates.values();
- descriptionDuplicates.forEach(duplicates => {
- if (duplicates.length > 1) {
- duplicates.forEach(duplicate => {
- duplicate.description = duplicate.editor.getDescription(); // fallback to full description if the short description still has duplicates
- });
- }
- });
-
return labels;
}
diff --git a/src/vs/workbench/parts/indentation/common/indentation.ts b/src/vs/workbench/common/editor/indentation.ts
similarity index 68%
rename from src/vs/workbench/parts/indentation/common/indentation.ts
rename to src/vs/workbench/common/editor/indentation.ts
index 2f95969bcf4..607df8c965d 100644
--- a/src/vs/workbench/parts/indentation/common/indentation.ts
+++ b/src/vs/workbench/common/editor/indentation.ts
@@ -5,11 +5,12 @@
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
-import { ICommonCodeEditor, EditorContextKeys } from 'vs/editor/common/editorCommon';
+import { ICommonCodeEditor, EditorContextKeys, ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, IActionOptions, EditorAction } from 'vs/editor/common/editorCommonExtensions';
-import { IndentationToSpacesCommand, IndentationToTabsCommand } from 'vs/workbench/parts/indentation/common/indentationCommands';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IModelService } from 'vs/editor/common/services/modelService';
+import { Range } from 'vs/editor/common/core/range';
+import { Selection } from 'vs/editor/common/core/selection';
@editorAction
export class IndentationToSpacesAction extends EditorAction {
@@ -160,3 +161,61 @@ export class DetectIndentation extends EditorAction {
model.detectIndentation(creationOpts.insertSpaces, creationOpts.tabSize);
}
}
+
+function getIndentationEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder, tabSize: number, tabsToSpaces: boolean): void {
+ if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
+ // Model is empty
+ return;
+ }
+
+ let spaces = '';
+ for (let i = 0; i < tabSize; i++) {
+ spaces += ' ';
+ }
+
+ const content = model.getLinesContent();
+ for (let i = 0; i < content.length; i++) {
+ let lastIndentationColumn = model.getLineFirstNonWhitespaceColumn(i + 1);
+ if (lastIndentationColumn === 0) {
+ lastIndentationColumn = model.getLineMaxColumn(i + 1);
+ }
+
+ const text = (tabsToSpaces ? content[i].substr(0, lastIndentationColumn).replace(/\t/ig, spaces) :
+ content[i].substr(0, lastIndentationColumn).replace(new RegExp(spaces, 'gi'), '\t')) +
+ content[i].substr(lastIndentationColumn);
+
+ builder.addEditOperation(new Range(i + 1, 1, i + 1, model.getLineMaxColumn(i + 1)), text);
+ }
+}
+
+export class IndentationToSpacesCommand implements ICommand {
+
+ private selectionId: string;
+
+ constructor(private selection: Selection, private tabSize: number) { }
+
+ public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {
+ this.selectionId = builder.trackSelection(this.selection);
+ getIndentationEditOperations(model, builder, this.tabSize, true);
+ }
+
+ public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection {
+ return helper.getTrackedSelection(this.selectionId);
+ }
+}
+
+export class IndentationToTabsCommand implements ICommand {
+
+ private selectionId: string;
+
+ constructor(private selection: Selection, private tabSize: number) { }
+
+ public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {
+ this.selectionId = builder.trackSelection(this.selection);
+ getIndentationEditOperations(model, builder, this.tabSize, false);
+ }
+
+ public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection {
+ return helper.getTrackedSelection(this.selectionId);
+ }
+}
diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts
index 4bb9cfe2147..9de6e754f45 100644
--- a/src/vs/workbench/common/editor/resourceEditorInput.ts
+++ b/src/vs/workbench/common/editor/resourceEditorInput.ts
@@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput, ITextEditorModel } from 'vs/workbench/common/editor';
import URI from 'vs/base/common/uri';
import { IReference } from 'vs/base/common/lifecycle';
-import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetry';
+import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils';
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts
index 919cd4ee181..8d037cb8adf 100644
--- a/src/vs/workbench/common/editor/untitledEditorInput.ts
+++ b/src/vs/workbench/common/editor/untitledEditorInput.ts
@@ -17,7 +17,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
-import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetry';
+import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils';
/**
* An editor input to be used for untitled text buffers.
diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts
index ad907944351..2340dc31350 100644
--- a/src/vs/workbench/electron-browser/shell.ts
+++ b/src/vs/workbench/electron-browser/shell.ts
@@ -22,7 +22,8 @@ import pkg from 'vs/platform/node/package';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench';
import { StorageService, inMemoryLocalStorageInstance } from 'vs/platform/storage/common/storageService';
-import { ITelemetryService, NullTelemetryService, configurationTelemetry, loadExperiments, lifecycleTelemetry } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService, configurationTelemetry, loadExperiments, lifecycleTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { IdleMonitor, UserStatus } from 'vs/platform/telemetry/browser/idleMonitor';
@@ -68,7 +69,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { CommandService } from 'vs/platform/commands/common/commandService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
-import { MainThreadModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
+import { WorkbenchModeServiceImpl } from 'vs/workbench/services/mode/common/workbenchModeService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { CrashReporter } from 'vs/workbench/electron-browser/crashReporter';
@@ -346,7 +347,7 @@ export class WorkbenchShell {
serviceCollection.set(IMarkerService, new SyncDescriptor(MarkerService));
- serviceCollection.set(IModeService, new SyncDescriptor(MainThreadModeServiceImpl));
+ serviceCollection.set(IModeService, new SyncDescriptor(WorkbenchModeServiceImpl));
serviceCollection.set(IModelService, new SyncDescriptor(ModelServiceImpl));
diff --git a/src/vs/workbench/electron-browser/workbench.main.ts b/src/vs/workbench/electron-browser/workbench.main.ts
index 51a158d0b31..a535aa73334 100644
--- a/src/vs/workbench/electron-browser/workbench.main.ts
+++ b/src/vs/workbench/electron-browser/workbench.main.ts
@@ -82,7 +82,6 @@ import 'vs/workbench/parts/emmet/browser/emmet.browser.contribution';
import 'vs/workbench/parts/emmet/node/emmet.contribution';
// Code Editor enhacements
-import 'vs/workbench/parts/indentation/common/indentation';
import 'vs/workbench/parts/codeEditor/codeEditor.contribution';
import 'vs/workbench/parts/execution/electron-browser/execution.contribution';
diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts
index ea687355b40..ab71366d4b7 100644
--- a/src/vs/workbench/parts/debug/common/debug.ts
+++ b/src/vs/workbench/parts/debug/common/debug.ts
@@ -276,6 +276,7 @@ export enum State {
export interface IDebugConfiguration {
allowBreakpointsEverywhere: boolean;
openExplorerOnEnd: boolean;
+ inlineValues: boolean;
}
export interface IGlobalConfig {
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 055036a7108..70ae4cd04b9 100644
--- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts
+++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts
@@ -176,6 +176,11 @@ configurationRegistry.registerConfiguration({
type: 'boolean',
description: nls.localize({ comment: ['This is the description for a setting'], key: 'openExplorerOnEnd' }, "Automatically open explorer view on the end of a debug session"),
default: false
+ },
+ 'debug.inlineValues': {
+ type: 'boolean',
+ description: nls.localize({ comment: ['This is the description for a setting'], key: 'inlineValues' }, "Show variable values inline in editor while debugging"),
+ default: false
}
}
});
diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts
index 824e53dc115..917641ad7f3 100644
--- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts
+++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts
@@ -17,21 +17,26 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { IModelDecorationOptions, MouseTargetType, IModelDeltaDecoration, TrackedRangeStickiness, IPosition } from 'vs/editor/common/editorCommon';
+import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { DebugHoverWidget } from 'vs/workbench/parts/debug/electron-browser/debugHover';
import { RemoveBreakpointAction, EditConditionalBreakpointAction, EnableBreakpointAction, DisableBreakpointAction, AddConditionalBreakpointAction } from 'vs/workbench/parts/debug/browser/debugActions';
-import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame } from 'vs/workbench/parts/debug/common/debug';
+import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration } from 'vs/workbench/parts/debug/common/debug';
import { BreakpointWidget } from 'vs/workbench/parts/debug/browser/breakpointWidget';
import { FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
+import { toNameValueMap, getDecorations, getWordToLineNumbersMap } from 'vs/workbench/parts/debug/electron-browser/debugInlineValues';
const HOVER_DELAY = 300;
const LAUNCH_JSON_REGEX = /launch\.json$/;
+const REMOVE_DECORATORS_DEBOUNCE_INTERVAL = 100; // If we receive a break in this interval, don't reset decorators as it causes a UI flash.
+const INLINE_DECORATOR_KEY = 'inlineDecorator';
@editorContribution
export class DebugEditorContribution implements IDebugEditorContribution {
@@ -45,6 +50,8 @@ export class DebugEditorContribution implements IDebugEditorContribution {
private breakpointHintDecoration: string[];
private breakpointWidget: BreakpointWidget;
private breakpointWidgetVisible: IContextKey;
+ private removeDecorationsTimeoutId = 0;
+ private wordToLineNumbersMap: Map;
private configurationWidget: FloatingClickWidget;
@@ -55,7 +62,9 @@ export class DebugEditorContribution implements IDebugEditorContribution {
@IInstantiationService private instantiationService: IInstantiationService,
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService private commandService: ICommandService,
- @ITelemetryService private telemetryService: ITelemetryService
+ @ICodeEditorService private codeEditorService: ICodeEditorService,
+ @ITelemetryService private telemetryService: ITelemetryService,
+ @IConfigurationService private configurationService: IConfigurationService
) {
this.breakpointHintDecoration = [];
this.hoverWidget = new DebugHoverWidget(this.editor, this.debugService, this.instantiationService);
@@ -65,6 +74,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
this.registerListeners();
this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService);
this.updateConfigurationWidgetVisibility();
+ this.codeEditorService.registerDecorationType(INLINE_DECORATOR_KEY, {});
}
private getContextMenuActions(breakpoint: IBreakpoint, uri: uri, lineNumber: number): TPromise {
@@ -200,6 +210,47 @@ export class DebugEditorContribution implements IDebugEditorContribution {
this.editor.updateOptions({ hover: true });
this.hideHoverWidget();
}
+
+ if (this.configurationService.getConfiguration('debug').inlineValues) {
+ this.updateInlineDecorators(sf);
+ }
+ }
+
+ private updateInlineDecorators(stackFrame: IStackFrame): void {
+ // Since step over, step out is a fast continue + break. Continue clears stack.
+ // This means we'll get a null stackFrame followed quickly by a valid stackFrame.
+ // Removing all decorators and adding them again causes a noticeable UI flash due to relayout and paint.
+ // We want to only remove inline decorations if a null stackFrame isn't followed by a valid stackFrame in a short interval.
+ clearTimeout(this.removeDecorationsTimeoutId);
+ if (!stackFrame) {
+ this.removeDecorationsTimeoutId = setTimeout(() => {
+ this.editor.removeDecorations(INLINE_DECORATOR_KEY);
+ this.wordToLineNumbersMap = null;
+ }, REMOVE_DECORATORS_DEBOUNCE_INTERVAL);
+ return;
+ }
+
+ // URI has changed, invalidate the editorWordRangeMap so its re-computed for the current model
+ if (stackFrame.source.uri.toString() !== this.editor.getModel().uri.toString()) {
+ this.wordToLineNumbersMap = null;
+ }
+
+ stackFrame.getScopes()
+ // Get all top level children in the scope chain
+ .then(scopes => TPromise.join(scopes.map(scope => scope.getChildren())))
+ .then(children => {
+ const editorModel = this.editor.getModel();
+ // Compute name-value map for all variables in scope chain
+ const expressions = [].concat.apply([], children);
+ const nameValueMap = toNameValueMap(expressions);
+ // Build wordRangeMap if not already computed for the editor model
+ if (!this.wordToLineNumbersMap) {
+ this.wordToLineNumbersMap = getWordToLineNumbersMap(editorModel);
+ }
+ // Compute decorators from nameValueMap and wordRangeMap and apply to editor
+ const decorators = getDecorations(nameValueMap, this.wordToLineNumbersMap);
+ this.editor.setDecorations(INLINE_DECORATOR_KEY, decorators);
+ });
}
private hideHoverWidget(): void {
diff --git a/src/vs/workbench/parts/debug/electron-browser/debugInlineValues.ts b/src/vs/workbench/parts/debug/electron-browser/debugInlineValues.ts
new file mode 100644
index 00000000000..ddb2864ee93
--- /dev/null
+++ b/src/vs/workbench/parts/debug/electron-browser/debugInlineValues.ts
@@ -0,0 +1,140 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IDecorationOptions, IModel } from 'vs/editor/common/editorCommon';
+import { StandardTokenType } from 'vs/editor/common/modes';
+import { IExpression } from 'vs/workbench/parts/debug/common/debug';
+
+export const MAX_INLINE_VALUE_LENGTH = 50; // Max string length of each inline 'x = y' string. If exceeded ... is added
+export const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added
+export const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We want to limit ourselves for perf reasons
+export const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped
+// LanguageConfigurationRegistry.getWordDefinition() return regexes that allow spaces and punctuation characters for languages like python
+// Using that approach is not viable so we are using a simple regex to look for word tokens.
+export const WORD_REGEXP = /[\$\_A-Za-z][\$\_A-Za-z0-9]*/g;
+
+export function toNameValueMap(expressions: IExpression[]): Map {
+ const result = new Map();
+ let valueCount = 0;
+
+ for (let expr of expressions) {
+ // Put ellipses in value if its too long. Preserve last char e.g "longstr…" or {a:true, b:true, …}
+ let value = expr.value;
+ if (value && value.length > MAX_INLINE_VALUE_LENGTH) {
+ value = value.substr(0, MAX_INLINE_VALUE_LENGTH) + '…' + value[value.length - 1];
+ }
+
+ result.set(expr.name, value);
+
+ // Limit the size of map. Too large can have a perf impact
+ if (++valueCount >= MAX_NUM_INLINE_VALUES) {
+ break;
+ }
+ }
+
+ return result;
+}
+
+export function getDecorations(nameValueMap: Map, wordToLineNumbersMap: Map): IDecorationOptions[] {
+ const lineToNamesMap: Map = new Map();
+ const decorations: IDecorationOptions[] = [];
+
+ // Compute unique set of names on each line
+ nameValueMap.forEach((value, name) => {
+ if (wordToLineNumbersMap.has(name)) {
+ for (let lineNumber of wordToLineNumbersMap.get(name)) {
+ if (!lineToNamesMap.has(lineNumber)) {
+ lineToNamesMap.set(lineNumber, []);
+ }
+
+ lineToNamesMap.get(lineNumber).push(name);
+ }
+ }
+ });
+
+ // Compute decorators for each line
+ lineToNamesMap.forEach((names, line) => {
+ // Wrap with 1em unicode space for readability
+ const contentText = '\u2003' + names.map(name => `${name} = ${nameValueMap.get(name)}`).join(', ') + '\u2003';
+ decorations.push(createDecoration(line, contentText));
+ });
+
+ return decorations;
+}
+
+function createDecoration(lineNumber: number, contentText: string): IDecorationOptions {
+ const margin = '10px';
+ const backgroundColor = 'rgba(255, 200, 0, 0.2)';
+ const lightForegroundColor = 'rgba(0, 0, 0, 0.5)';
+ const darkForegroundColor = 'rgba(255, 255, 255, 0.5)';
+
+ // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line
+ if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) {
+ contentText = contentText.substr(0, MAX_INLINE_DECORATOR_LENGTH) + '...';
+ }
+
+ return {
+ range: {
+ startLineNumber: lineNumber,
+ endLineNumber: lineNumber,
+ startColumn: Number.MAX_VALUE,
+ endColumn: Number.MAX_VALUE
+ },
+ renderOptions: {
+ dark: {
+ after: {
+ contentText,
+ backgroundColor,
+ color: darkForegroundColor,
+ margin
+ }
+ },
+ light: {
+ after: {
+ contentText,
+ backgroundColor,
+ color: lightForegroundColor,
+ margin
+ }
+ }
+ }
+ };
+}
+
+export function getWordToLineNumbersMap(model: IModel): Map {
+ const result = new Map();
+
+ // For every word in every line, map its ranges for fast lookup
+ for (let lineNumber = 1, len = model.getLineCount(); lineNumber <= len; ++lineNumber) {
+ const lineContent = model.getLineContent(lineNumber);
+
+ // If line is too long then skip the line
+ if (lineContent.length > MAX_TOKENIZATION_LINE_LEN) {
+ continue;
+ }
+
+ const lineTokens = model.getLineTokens(lineNumber);
+ for (let token = lineTokens.firstToken(); !!token; token = token.next()) {
+ const tokenStr = lineContent.substring(token.startOffset, token.endOffset);
+
+ // Token is a word and not a comment
+ if (token.tokenType === StandardTokenType.Other) {
+ WORD_REGEXP.lastIndex = 0; // We assume tokens will usually map 1:1 to words if they match
+ const wordMatch = WORD_REGEXP.exec(tokenStr);
+
+ if (wordMatch) {
+ const word = wordMatch[0];
+ if (!result.has(word)) {
+ result.set(word, []);
+ }
+
+ result.get(word).push(lineNumber);
+ }
+ }
+ }
+ }
+
+ return result;
+}
diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts
index 947db833ebc..59150906ba0 100644
--- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts
+++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts
@@ -523,7 +523,7 @@ export class DebugService implements debug.IDebugService {
this.telemetryService.publicLog('debugService/addReplExpression');
return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
- .then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame));
+ .then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess));
}
public removeReplExpressions(): void {
@@ -537,7 +537,7 @@ export class DebugService implements debug.IDebugService {
public renameWatchExpression(id: string, newName: string): TPromise {
return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName)
// Evaluate all watch expressions and fetch variables again since watch expression evaluation might have changed some.
- .then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame));
+ .then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess));
}
public moveWatchExpression(id: string, position: number): void {
@@ -779,7 +779,7 @@ export class DebugService implements debug.IDebugService {
this.lastTaskEvent = null;
});
- if (filteredTasks[0].isWatching) {
+ if (filteredTasks[0].isBackground) {
return new TPromise((c, e) => this.taskService.addOneTimeDisposableListener(TaskServiceEvents.Inactive, () => c(null)));
}
diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts
index 4cba885380f..7578afeb9b9 100644
--- a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts
+++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts
@@ -559,6 +559,9 @@ export class CallStackRenderer implements IRenderer {
private renderStackFrame(stackFrame: debug.IStackFrame, data: IStackFrameTemplateData): void {
stackFrame.source.deemphasize ? dom.addClass(data.stackFrame, 'disabled') : dom.removeClass(data.stackFrame, 'disabled');
data.file.title = stackFrame.source.raw.path || stackFrame.source.name;
+ if (stackFrame.source.raw.origin) {
+ data.file.title += `\n${stackFrame.source.raw.origin}`;
+ }
data.label.textContent = stackFrame.name;
data.label.title = stackFrame.name;
data.fileName.textContent = getSourceName(stackFrame.source, this.contextService);
diff --git a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts
index 6f6340a3e55..651944976f6 100644
--- a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts
+++ b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts
@@ -24,7 +24,7 @@ export class TerminalSupport {
let delay = 0;
if (!TerminalSupport.integratedTerminalInstance) {
- TerminalSupport.integratedTerminalInstance = terminalService.createInstance(args.title || nls.localize('debuggee', "debuggee"));
+ TerminalSupport.integratedTerminalInstance = terminalService.createInstance({ executable: args.title || nls.localize('debuggee', "debuggee") });
delay = 2000; // delay sendText so that the newly created terminal is ready.
}
if (!TerminalSupport.terminalDisposedListener) {
diff --git a/src/vs/workbench/parts/debug/test/electron-browser/debugInlineValues.test.ts b/src/vs/workbench/parts/debug/test/electron-browser/debugInlineValues.test.ts
new file mode 100644
index 00000000000..93f3930ce80
--- /dev/null
+++ b/src/vs/workbench/parts/debug/test/electron-browser/debugInlineValues.test.ts
@@ -0,0 +1,189 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as assert from 'assert';
+// import { Model as EditorModel } from 'vs/editor/common/model/model';
+// import { IModel } from 'vs/editor/common/editorCommon';
+import { StandardTokenType } from 'vs/editor/common/modes';
+// import { LineTokens } from 'vs/editor/common/core/lineTokens';
+import { IExpression } from 'vs/workbench/parts/debug/common/debug';
+import * as inlineValues from 'vs/workbench/parts/debug/electron-browser/debugInlineValues';
+
+// Test data
+// const testLine = 'function doit(everything, is, awesome, awesome, when, youre, part, of, a, team){}';
+const testNameValueMap = new Map();
+
+setup(() => {
+ testNameValueMap.set('everything', '{emmet: true, batman: true, legoUniverse: true}');
+ testNameValueMap.set('is', '15');
+ testNameValueMap.set('awesome', '"aweeeeeeeeeeeeeeeeeeeeeeeeeeeeeeesome…"');
+ testNameValueMap.set('when', 'true');
+ testNameValueMap.set('youre', '"Yes I mean you"');
+ testNameValueMap.set('part', '"𝄞 ♪ ♫"');
+});
+
+suite('Debug - Inline Value Decorators', () => {
+ test('getNameValueMapFromScopeChildren trims long values', () => {
+ const expressions = [
+ createExpression('hello', 'world'),
+ createExpression('blah', createLongString())
+ ];
+
+ const nameValueMap = inlineValues.toNameValueMap(expressions);
+ const expectedNameValueMap = new Map();
+ expectedNameValueMap.set('hello', 'world');
+ expectedNameValueMap.set('blah', '"blah blah blah blah blah blah blah blah blah blah…"');
+
+ // Ensure blah is capped and ellipses added
+ assert.deepEqual(nameValueMap, expectedNameValueMap);
+ });
+
+ test('getNameValueMapFromScopeChildren caps scopes to a MAX_NUM_INLINE_VALUES limit', () => {
+ const scopeChildren: IExpression[][] = new Array(5);
+ const expectedNameValueMap: Map = new Map();
+
+ // 10 Stack Frames with a 100 scope expressions each
+ // JS Global Scope has 700+ expressions so this is close to a real world scenario
+ for (let i = 0; i < scopeChildren.length; i++) {
+ const expressions = new Array(50);
+
+ for (let j = 0; j < expressions.length; ++j) {
+ const name = `name${i}.${j}`;
+ const val = `val${i}.${j}`;
+ expressions[j] = createExpression(name, val);
+
+ if ((i * expressions.length + j) < inlineValues.MAX_NUM_INLINE_VALUES) {
+ expectedNameValueMap.set(name, val);
+ }
+ }
+
+ scopeChildren[i] = expressions;
+ }
+
+ const expressions = [].concat.apply([], scopeChildren);
+ const nameValueMap = inlineValues.toNameValueMap(expressions);
+
+ assert.deepEqual(nameValueMap, expectedNameValueMap);
+ });
+
+ // test('getDecorators returns correct decorator afterText', () => {
+ // const lineContent = 'console.log(everything, part, part);'; // part shouldn't be duplicated
+ // const lineNumber = 1;
+ // const wordToLinesMap = getWordToLineMap(lineNumber, lineContent);
+ // const decorators = inlineValues.getDecorations(testNameValueMap, wordToLinesMap);
+ // const expectedDecoratorText = ' everything = {emmet: true, batman: true, legoUniverse: true}, part = "𝄞 ♪ ♫" ';
+ // assert.equal(decorators[0].renderOptions.dark.after.contentText, expectedDecoratorText);
+ // });
+
+ // test('getEditorWordRangeMap ignores comments and long lines', () => {
+ // const expectedWords = 'function, doit, everything, is, awesome, when, youre, part, of, a, team'.split(', ');
+ // const editorModel = EditorModel.createFromString(`/** Copyright comment */\n \n${testLine}\n// Test comment\n${createLongString()}\n`);
+ // mockEditorModelLineTokens(editorModel);
+
+ // const wordRangeMap = inlineValues.getWordToLineNumbersMap(editorModel);
+ // const words = Object.keys(wordRangeMap);
+ // assert.deepEqual(words, expectedWords);
+ // });
+});
+
+// Test helpers
+
+function createExpression(name: string, value: string): IExpression {
+ return {
+ name,
+ value,
+ getId: () => name,
+ hasChildren: false,
+ getChildren: null
+ };
+}
+
+function createLongString(): string {
+ let longStr = '';
+ for (let i = 0; i < 100; ++i) {
+ longStr += 'blah blah blah ';
+ }
+ return `"${longStr}"`;
+}
+
+// Simple word range creator that maches wordRegex throughout string
+// function getWordToLineMap(lineNumber: number, lineContent: string): Map {
+// const result = new Map();
+// const wordRegexp = inlineValues.WORD_REGEXP;
+// wordRegexp.lastIndex = 0; // Reset matching
+
+// while (true) {
+// const wordMatch = wordRegexp.exec(lineContent);
+// if (!wordMatch) {
+// break;
+// }
+// const word = wordMatch[0];
+
+// if (!result.has(word)) {
+// result.set(word, []);
+// }
+
+// result.get(word).push(lineNumber);
+// }
+
+// return result;
+// }
+
+interface MockToken {
+ tokenType: StandardTokenType;
+ startOffset: number;
+ endOffset: number;
+}
+
+// // Simple tokenizer that separates comments from words
+// function mockLineTokens(lineContent: string): LineTokens {
+// const tokens: MockToken[] = [];
+
+// if (lineContent.match(/^\s*\/(\/|\*)/)) {
+// tokens.push({
+// tokenType: StandardTokenType.Comment,
+// startOffset: 0,
+// endOffset: lineContent.length
+// });
+// }
+// // Tokenizer should ignore pure whitespace token
+// else if (lineContent.match(/^\s+$/)) {
+// tokens.push({
+// tokenType: StandardTokenType.Other,
+// startOffset: 0,
+// endOffset: lineContent.length
+// });
+// }
+// else {
+// const wordRegexp = inlineValues.WORD_REGEXP;
+// wordRegexp.lastIndex = 0;
+
+// while (true) {
+// const wordMatch = wordRegexp.exec(lineContent);
+// if (!wordMatch) {
+// break;
+// }
+
+// tokens.push({
+// tokenType: StandardTokenType.String,
+// startOffset: wordMatch.index,
+// endOffset: wordMatch.index + wordMatch[0].length
+// });
+// }
+// }
+
+// return {
+// getLineContent: (): string => lineContent,
+// getTokenCount: (): number => tokens.length,
+// getTokenStartOffset: (tokenIndex: number): number => tokens[tokenIndex].startOffset,
+// getTokenEndOffset: (tokenIndex: number): number => tokens[tokenIndex].endOffset,
+// getStandardTokenType: (tokenIndex: number): StandardTokenType => tokens[tokenIndex].tokenType
+// };
+// };
+
+// function mockEditorModelLineTokens(editorModel: IModel): void {
+// const linesContent = editorModel.getLinesContent();
+// editorModel.getLineTokens = (lineNumber: number): LineTokens => mockLineTokens(linesContent[lineNumber - 1]);
+// }
diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts
index eba4ecb0047..e9280041341 100644
--- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts
+++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts
@@ -18,7 +18,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate';
import { LocalExtensionType, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
-import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import { ToggleViewletAction } from 'vs/workbench/browser/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -29,7 +29,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IExtensionService, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import URI from 'vs/base/common/uri';
-
+import { CommandsRegistry } from 'vs/platform/commands/common/commands';
export class InstallAction extends Action {
@@ -1290,4 +1290,15 @@ export class EnableAllWorkpsaceAction extends Action {
super.dispose();
this.disposables = dispose(this.disposables);
}
-}
\ No newline at end of file
+}
+
+CommandsRegistry.registerCommand('workbench.extensions.action.showLanguageExtensions', function (accessor: ServicesAccessor, fileExtension: string) {
+ const viewletService = accessor.get(IViewletService);
+
+ return viewletService.openViewlet(VIEWLET_ID, true)
+ .then(viewlet => viewlet as IExtensionsViewlet)
+ .then(viewlet => {
+ viewlet.search(`ext:${fileExtension.replace(/^\./, '')}`);
+ viewlet.focus();
+ });
+});
diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts
index e0ea965c2dd..2dd869dcd19 100644
--- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts
+++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts
@@ -149,7 +149,7 @@ const enableAllWorkspaceAction = new SyncActionDescriptor(EnableAllWorkpsaceActi
actionRegistry.registerWorkbenchAction(enableAllWorkspaceAction, 'Extensions: Enable All (Workspace)', ExtensionsLabel);
const checkForUpdatesAction = new SyncActionDescriptor(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL);
-actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: ${CheckForUpdatesAction.LABEL}`, ExtensionsLabel);
+actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: Check for Updates`, ExtensionsLabel);
Registry.as(ConfigurationExtensions.Configuration)
.registerConfiguration({
diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css
index 309f8da8801..42b78301463 100644
--- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css
+++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css
@@ -27,6 +27,12 @@
height: calc(100% - 38px);
}
+.extensions-viewlet > .extensions.hidden,
+.extensions-viewlet > .message.hidden {
+ display: none;
+ visibility: hidden;
+}
+
.extensions-viewlet > .message {
padding: 5px 9px 5px 16px;
cursor: default;
diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts
index 7dc3eb0d8a6..d7aeefd8da3 100644
--- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts
+++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts
@@ -841,7 +841,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
}
const extension = result.firstPage[0];
- this.open(extension).done(null, error => this.onError(error));
+ const promises = [this.open(extension)];
+
+ if (this.local.every(local => local.identifier !== extension.identifier)) {
+ promises.push(this.install(extension));
+ }
+
+ TPromise.join(promises)
+ .done(null, error => this.onError(error));
});
}
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 a8dae077849..9867c033843 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
@@ -24,7 +24,8 @@ import { IURLService } from 'vs/platform/url/common/url';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { Emitter } from 'vs/base/common/event';
import { IPager } from 'vs/base/common/paging';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IWorkspaceContextService, WorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts
index 7147b524286..38756fc05f3 100644
--- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts
+++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts
@@ -25,7 +25,8 @@ import { IURLService } from 'vs/platform/url/common/url';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import Event, { Emitter } from 'vs/base/common/event';
import { IPager } from 'vs/base/common/paging';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { IWorkspaceContextService, WorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
import { IChoiceService } from 'vs/platform/message/common/message';
diff --git a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css
index 7429a126d46..f4eb9b1fdd1 100644
--- a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css
+++ b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css
@@ -48,6 +48,11 @@
visibility: hidden;
}
+.explorer-viewlet .header .monaco-count-badge.hidden {
+ display: none;
+ visibility: hidden;
+}
+
.explorer-folders-view .monaco-tree-row > .content {
display: inline-block;
}
diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts
index 0679e33c368..e7ddeb72683 100644
--- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts
+++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts
@@ -652,10 +652,6 @@ export class FileSorter implements ISorter {
return 1;
}
- if (statA.isDirectory && statB.isDirectory) {
- return statA.name.toLowerCase().localeCompare(statB.name.toLowerCase());
- }
-
if (statA instanceof NewStatPlaceholder) {
return -1;
}
diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts
index fe00a431112..a9b5b4215cd 100644
--- a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts
+++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts
@@ -17,7 +17,7 @@ import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent }
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
-import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetry';
+import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils';
/**
* A file editor input is the input type for the file editor of file system resources.
diff --git a/src/vs/workbench/parts/html/browser/webview-pre.js b/src/vs/workbench/parts/html/browser/webview-pre.js
index 94175dc2e91..d19b93a51ec 100644
--- a/src/vs/workbench/parts/html/browser/webview-pre.js
+++ b/src/vs/workbench/parts/html/browser/webview-pre.js
@@ -87,7 +87,7 @@ document.addEventListener("DOMContentLoaded", function (event) {
' while (node) {',
' if (node.tagName === "A" && node.href) {',
' let baseElement = window.document.getElementsByTagName("base")[0];',
- ' if (baseElement && node.href.indexOf(baseElement.href) >= 0 && node.hash) {',
+ ' if (node.hash && (node.getAttribute("href") === node.hash || (baseElement && node.href.indexOf(baseElement.href) >= 0))) {',
' let scrollTarget = window.document.getElementById(node.hash.substr(1, node.hash.length - 1));',
' if (scrollTarget) {',
' scrollTarget.scrollIntoView();',
diff --git a/src/vs/workbench/parts/indentation/common/indentationCommands.ts b/src/vs/workbench/parts/indentation/common/indentationCommands.ts
deleted file mode 100644
index 4c2d164fc66..00000000000
--- a/src/vs/workbench/parts/indentation/common/indentationCommands.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { Range } from 'vs/editor/common/core/range';
-import { ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon';
-import { Selection } from 'vs/editor/common/core/selection';
-
-function getIndentationEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder, tabSize: number, tabsToSpaces: boolean): void {
- if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
- // Model is empty
- return;
- }
-
- let spaces = '';
- for (let i = 0; i < tabSize; i++) {
- spaces += ' ';
- }
-
- const content = model.getLinesContent();
- for (let i = 0; i < content.length; i++) {
- let lastIndentationColumn = model.getLineFirstNonWhitespaceColumn(i + 1);
- if (lastIndentationColumn === 0) {
- lastIndentationColumn = model.getLineMaxColumn(i + 1);
- }
-
- const text = (tabsToSpaces ? content[i].substr(0, lastIndentationColumn).replace(/\t/ig, spaces) :
- content[i].substr(0, lastIndentationColumn).replace(new RegExp(spaces, 'gi'), '\t')) +
- content[i].substr(lastIndentationColumn);
-
- builder.addEditOperation(new Range(i + 1, 1, i + 1, model.getLineMaxColumn(i + 1)), text);
- }
-}
-
-export class IndentationToSpacesCommand implements ICommand {
-
- private selectionId: string;
-
- constructor(private selection: Selection, private tabSize: number) { }
-
- public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {
- this.selectionId = builder.trackSelection(this.selection);
- getIndentationEditOperations(model, builder, this.tabSize, true);
- }
-
- public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection {
- return helper.getTrackedSelection(this.selectionId);
- }
-}
-
-export class IndentationToTabsCommand implements ICommand {
-
- private selectionId: string;
-
- constructor(private selection: Selection, private tabSize: number) { }
-
- public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {
- this.selectionId = builder.trackSelection(this.selection);
- getIndentationEditOperations(model, builder, this.tabSize, false);
- }
-
- public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection {
- return helper.getTrackedSelection(this.selectionId);
- }
-}
diff --git a/src/vs/workbench/parts/markers/browser/media/markers.css b/src/vs/workbench/parts/markers/browser/media/markers.css
index 2cdcded90d2..b270a4d3e81 100644
--- a/src/vs/workbench/parts/markers/browser/media/markers.css
+++ b/src/vs/workbench/parts/markers/browser/media/markers.css
@@ -41,6 +41,12 @@
height: 100%;
}
+.markers-panel .markers-panel-container .tree-container.hidden,
+.markers-panel .markers-panel-container .message-box-container.hidden {
+ display: none;
+ visibility: hidden;
+}
+
.markers-panel .markers-panel-container .tree-container .markers-panel-tree-entry {
display: flex;
line-height: 22px;
diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css
index 6c31b27d5e7..b4c41817652 100644
--- a/src/vs/workbench/parts/preferences/browser/media/preferences.css
+++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css
@@ -156,6 +156,11 @@
cursor: pointer;
}
+.monaco-editor .edit-preferences-widget.hidden {
+ display: none;
+ visibility: hidden;
+}
+
.monaco-editor.hc-black .edit-preferences-widget,
.monaco-editor.vs-dark .edit-preferences-widget {
background: url('edit_inverse.svg') center center no-repeat;
diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts
index ca52028f261..82990a8d2dd 100644
--- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts
+++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts
@@ -12,7 +12,8 @@ import { PPromise } from 'vs/base/common/winjs.base';
import { SearchModel } from 'vs/workbench/parts/search/common/searchModel';
import URI from 'vs/base/common/uri';
import { IFileMatch, ILineMatch, ISearchService, ISearchComplete, ISearchProgressItem, IUncachedSearchStats } from 'vs/platform/search/common/search';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { Range } from 'vs/editor/common/core/range';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
diff --git a/src/vs/workbench/parts/search/test/common/searchResult.test.ts b/src/vs/workbench/parts/search/test/common/searchResult.test.ts
index 15ad9a43fb7..07b7f09b73d 100644
--- a/src/vs/workbench/parts/search/test/common/searchResult.test.ts
+++ b/src/vs/workbench/parts/search/test/common/searchResult.test.ts
@@ -10,7 +10,8 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
import { Match, FileMatch, SearchResult, SearchModel } from 'vs/workbench/parts/search/common/searchModel';
import URI from 'vs/base/common/uri';
import { IFileMatch, ILineMatch } from 'vs/platform/search/common/search';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { Range } from 'vs/editor/common/core/range';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
diff --git a/src/vs/editor/contrib/suggest/common/snippetCompletion.ts b/src/vs/workbench/parts/snippets/common/snippetCompletion.ts
similarity index 99%
rename from src/vs/editor/contrib/suggest/common/snippetCompletion.ts
rename to src/vs/workbench/parts/snippets/common/snippetCompletion.ts
index 8fb21f6bb21..c3e70c3f794 100644
--- a/src/vs/editor/contrib/suggest/common/snippetCompletion.ts
+++ b/src/vs/workbench/parts/snippets/common/snippetCompletion.ts
@@ -120,4 +120,4 @@ class InsertSnippetAction extends EditorAction {
}
});
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts
index 9c05c47a36c..135f5abc346 100644
--- a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts
+++ b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
+import 'vs/workbench/parts/snippets/common/snippetCompletion';
import nls = require('vs/nls');
import winjs = require('vs/base/common/winjs.base');
import paths = require('vs/base/common/paths');
diff --git a/src/vs/workbench/parts/tasks/common/languageServiceTaskSystem.ts b/src/vs/workbench/parts/tasks/common/languageServiceTaskSystem.ts
deleted file mode 100644
index 3fb0a2240df..00000000000
--- a/src/vs/workbench/parts/tasks/common/languageServiceTaskSystem.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * 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 { TPromise, Promise } from 'vs/base/common/winjs.base';
-import { TerminateResponse } from 'vs/base/common/processes';
-
-import { IMode } from 'vs/editor/common/modes';
-import { EventEmitter } from 'vs/base/common/eventEmitter';
-
-import { ITaskSystem, ITaskSummary, TaskDescription, TelemetryEvent, Triggers, TaskConfiguration, ITaskExecuteResult, TaskExecuteKind } from 'vs/workbench/parts/tasks/common/taskSystem';
-import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
-import { IModeService } from 'vs/editor/common/services/modeService';
-
-export interface LanguageServiceTaskConfiguration extends TaskConfiguration {
- modes: string[];
-}
-
-export class LanguageServiceTaskSystem extends EventEmitter implements ITaskSystem {
-
- public static TelemetryEventName: string = 'taskService';
-
- private configuration: LanguageServiceTaskConfiguration;
- private telemetryService: ITelemetryService;
- private modeService: IModeService;
-
- constructor(configuration: LanguageServiceTaskConfiguration, telemetryService: ITelemetryService, modeService: IModeService) {
- super();
- this.configuration = configuration;
- this.telemetryService = telemetryService;
- this.modeService = modeService;
- }
-
- public build(): ITaskExecuteResult {
- return this.processMode((mode) => {
- return null;
- }, 'build', Triggers.shortcut);
- }
-
- public rebuild(): ITaskExecuteResult {
- return this.processMode((mode) => {
- return null;
- }, 'rebuild', Triggers.shortcut);
- }
-
- public clean(): ITaskExecuteResult {
- return this.processMode((mode) => {
- return null;
- }, 'clean', Triggers.shortcut);
- }
-
- public runTest(): ITaskExecuteResult {
- return { kind: TaskExecuteKind.Started, promise: TPromise.wrapError('Not implemented yet.') };
- }
-
- public run(taskIdentifier: string): ITaskExecuteResult {
- return { kind: TaskExecuteKind.Started, promise: TPromise.wrapError('Not implemented yet.') };
- }
-
- public isActive(): TPromise {
- return TPromise.as(false);
- }
-
- public isActiveSync(): boolean {
- return false;
- }
-
- public canAutoTerminate(): boolean {
- return false;
- }
-
- public terminate(): TPromise {
- return TPromise.as({ success: true });
- }
-
- public terminateSync(): TerminateResponse {
- return { success: true };
- }
-
- public tasks(): TPromise {
- let result: TaskDescription[] = [];
- return TPromise.as(result);
- }
-
- private processMode(fn: (mode: IMode) => Promise, taskName: string, trigger: string): ITaskExecuteResult {
- let telemetryEvent: TelemetryEvent = {
- trigger: trigger,
- command: 'languageService',
- success: true
- };
- return {
- kind: TaskExecuteKind.Started, started: {}, promise: Promise.join(this.configuration.modes.map((mode) => {
- return this.modeService.getOrCreateMode(mode);
- })).then((modes: IMode[]) => {
- let promises: Promise[] = [];
- modes.forEach((mode) => {
- let promise = fn(mode);
- if (promise) {
- promises.push(promise);
- }
- });
- return Promise.join(promises);
- }).then((value) => {
- this.telemetryService.publicLog(LanguageServiceTaskSystem.TelemetryEventName, telemetryEvent);
- return value;
- }, (err) => {
- telemetryEvent.success = false;
- this.telemetryService.publicLog(LanguageServiceTaskSystem.TelemetryEventName, telemetryEvent);
- return Promise.wrapError(err);
- })
- };
- }
-}
diff --git a/src/vs/workbench/parts/tasks/common/taskSystem.ts b/src/vs/workbench/parts/tasks/common/taskSystem.ts
index 420f753685c..186e190fbe8 100644
--- a/src/vs/workbench/parts/tasks/common/taskSystem.ts
+++ b/src/vs/workbench/parts/tasks/common/taskSystem.ts
@@ -98,9 +98,9 @@ export interface TaskDescription {
args?: string[];
/**
- * Whether the task is running in watching mode or not.
+ * Whether the task is a background task or not.
*/
- isWatching?: boolean;
+ isBackground?: boolean;
/**
* Whether the task should prompt on close for confirmation if running.
@@ -201,7 +201,7 @@ export interface ITaskExecuteResult {
};
active?: {
same: boolean;
- watching: boolean;
+ background: boolean;
};
}
@@ -242,5 +242,5 @@ export interface TaskConfiguration {
/**
* The build system to use. If omitted program is used.
*/
- buildSystem?: string;
+ _runner?: string;
}
\ No newline at end of file
diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts
index 57bbc37b561..282718bc334 100644
--- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts
+++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts
@@ -59,13 +59,15 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IOutputService, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannel } from 'vs/workbench/parts/output/common/output';
+import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal';
+
import { ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskConfiguration, TaskDescription, TaskSystemEvents } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskService, TaskServiceEvents } from 'vs/workbench/parts/tasks/common/taskService';
import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates';
-import { LanguageServiceTaskSystem, LanguageServiceTaskConfiguration } from 'vs/workbench/parts/tasks/common/languageServiceTaskSystem';
import * as FileConfig from 'vs/workbench/parts/tasks/node/processRunnerConfiguration';
import { ProcessRunnerSystem } from 'vs/workbench/parts/tasks/node/processRunnerSystem';
+import { TerminalTaskSystem } from './terminalTaskSystem';
import { ProcessRunnerDetector } from 'vs/workbench/parts/tasks/node/processRunnerDetector';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -631,7 +633,9 @@ class TaskService extends EventEmitter implements ITaskService {
@IModelService modelService: IModelService, @IExtensionService extensionService: IExtensionService,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IEnvironmentService private environmentService: IEnvironmentService,
- @IConfigurationResolverService private configurationResolverService: IConfigurationResolverService) {
+ @IConfigurationResolverService private configurationResolverService: IConfigurationResolverService,
+ @ITerminalService private terminalService: ITerminalService
+ ) {
super();
this.modeService = modeService;
@@ -740,10 +744,15 @@ class TaskService extends EventEmitter implements ITaskService {
throw new TaskError(Severity.Info, nls.localize('TaskSystem.noConfiguration', 'No task runner configured.'), TaskErrors.NotConfigured);
}
let result: ITaskSystem = null;
- if (config.buildSystem === 'service') {
- result = new LanguageServiceTaskSystem(config, this.telemetryService, this.modeService);
- } else if (this.isRunnerConfig(config)) {
+ if (this.isRunnerConfig(config)) {
result = new ProcessRunnerSystem(config, this.markerService, this.modelService, this.telemetryService, this.outputService, this.configurationResolverService, TaskService.OutputChannelId, clearOutput);
+ } else if (this.isTerminalConfig(config)) {
+ result = new TerminalTaskSystem(
+ config,
+ this.terminalService, this.outputService, this.markerService,
+ this.modelService, this.configurationResolverService, this.telemetryService,
+ TaskService.OutputChannelId
+ );
}
if (result === null) {
this._taskSystemPromise = null;
@@ -776,7 +785,11 @@ class TaskService extends EventEmitter implements ITaskService {
}
private isRunnerConfig(config: TaskConfiguration): boolean {
- return !config.buildSystem || config.buildSystem === 'program';
+ return !config._runner || config._runner === 'program';
+ }
+
+ private isTerminalConfig(config: TaskConfiguration): boolean {
+ return config._runner === 'terminal';
}
private hasDetectorSupport(config: FileConfig.ExternalTaskRunnerConfiguration): boolean {
@@ -819,14 +832,14 @@ class TaskService extends EventEmitter implements ITaskService {
}
private executeTarget(fn: (taskSystem: ITaskSystem) => ITaskExecuteResult): TPromise {
- return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved
- return this.configurationService.reloadConfiguration().then(() => { // make sure configuration is up to date
+ return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved
+ return this.configurationService.reloadConfiguration().then(() => { // make sure configuration is up to date
return this.taskSystemPromise.
then((taskSystem) => {
let executeResult = fn(taskSystem);
if (executeResult.kind === TaskExecuteKind.Active) {
let active = executeResult.active;
- if (active.same && active.watching) {
+ if (active.same && active.background) {
this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame', 'The task is already active and in watch mode. To terminate the task use `F1 > terminate task`'));
} else {
throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is an active running task right now. Terminate it first before executing another task.'), TaskErrors.RunningTask);
diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts
new file mode 100644
index 00000000000..5b54dec1da2
--- /dev/null
+++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts
@@ -0,0 +1,464 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 path = require('path');
+
+import * as nls from 'vs/nls';
+import * as Objects from 'vs/base/common/objects';
+import { CharCode } from 'vs/base/common/charCode';
+import * as Platform from 'vs/base/common/platform';
+import * as Async from 'vs/base/common/async';
+import { TPromise } from 'vs/base/common/winjs.base';
+import { IStringDictionary } from 'vs/base/common/collections';
+import Severity from 'vs/base/common/severity';
+import { EventEmitter } from 'vs/base/common/eventEmitter';
+import { IDisposable, dispose } from 'vs/base/common/lifecycle';
+import { TerminateResponse } from 'vs/base/common/processes';
+
+import { IMarkerService } from 'vs/platform/markers/common/markers';
+import { ValidationStatus } from 'vs/base/common/parsers';
+import { IModelService } from 'vs/editor/common/services/modelService';
+import { ProblemMatcher } from 'vs/platform/markers/common/problemMatcher';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+
+import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
+import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
+import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
+import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common/output';
+import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors';
+import { ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskRunnerConfiguration, TaskDescription, ShowOutput, TelemetryEvent, Triggers, TaskSystemEvents, TaskEvent, TaskType } from 'vs/workbench/parts/tasks/common/taskSystem';
+import * as FileConfig from '../node/processRunnerConfiguration';
+
+interface TerminalData {
+ terminal: ITerminalInstance;
+ promise: TPromise;
+}
+
+class TerminalDecoder {
+ // See https://en.wikipedia.org/wiki/ANSI_escape_code & http://stackoverflow.com/questions/25189651/how-to-remove-ansi-control-chars-vt100-from-a-java-string &
+ // https://www.npmjs.com/package/strip-ansi
+ private static ANSI_CONTROL_SEQUENCE: RegExp = /\x1b[[()#;?]*(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
+
+ private remaining: string;
+
+ public write(data: string): string[] {
+ let result: string[] = [];
+ let value = this.remaining
+ ? this.remaining + data.replace(TerminalDecoder.ANSI_CONTROL_SEQUENCE, '')
+ : data.replace(TerminalDecoder.ANSI_CONTROL_SEQUENCE, '');
+
+ if (value.length < 1) {
+ return result;
+ }
+ let start = 0;
+ let ch: number;
+ while (start < value.length && ((ch = value.charCodeAt(start)) === CharCode.CarriageReturn || ch === CharCode.LineFeed)) {
+ start++;
+ }
+ let idx = start;
+ while (idx < value.length) {
+ ch = value.charCodeAt(idx);
+ if (ch === CharCode.CarriageReturn || ch === CharCode.LineFeed) {
+ result.push(value.substring(start, idx));
+ idx++;
+ while (idx < value.length && ((ch = value.charCodeAt(idx)) === CharCode.CarriageReturn || ch === CharCode.LineFeed)) {
+ idx++;
+ }
+ start = idx;
+ } else {
+ idx++;
+ }
+ }
+ this.remaining = start < value.length ? value.substr(start) : null;
+ return result;
+ }
+
+ public end(): string {
+ return this.remaining;
+ }
+}
+export class TerminalTaskSystem extends EventEmitter implements ITaskSystem {
+
+ public static TelemetryEventName: string = 'taskService';
+
+ private validationStatus: ValidationStatus;
+ private buildTaskIdentifier: string;
+ private testTaskIdentifier: string;
+ private configuration: TaskRunnerConfiguration;
+
+ private outputChannel: IOutputChannel;
+ private activeTasks: IStringDictionary;
+
+ constructor(private fileConfig: FileConfig.ExternalTaskRunnerConfiguration, private terminalService: ITerminalService, private outputService: IOutputService,
+ private markerService: IMarkerService, private modelService: IModelService, private configurationResolverService: IConfigurationResolverService,
+ private telemetryService: ITelemetryService, outputChannelId: string) {
+ super();
+
+ this.outputChannel = this.outputService.getChannel(outputChannelId);
+ this.clearOutput();
+ this.activeTasks = Object.create(null);
+
+ let parseResult = FileConfig.parse(fileConfig, this);
+ this.validationStatus = parseResult.validationStatus;
+ this.configuration = parseResult.configuration;
+ this.buildTaskIdentifier = parseResult.defaultBuildTaskIdentifier;
+ this.testTaskIdentifier = parseResult.defaultTestTaskIdentifier;
+
+ if (!this.validationStatus.isOK()) {
+ this.showOutput();
+ }
+ }
+
+ public log(value: string): void {
+ this.outputChannel.append(value + '\n');
+ }
+
+ private showOutput(): void {
+ this.outputChannel.show(true);
+ }
+
+ private clearOutput(): void {
+ this.outputChannel.clear();
+ }
+
+ public build(): ITaskExecuteResult {
+ if (!this.buildTaskIdentifier) {
+ throw new TaskError(Severity.Info, nls.localize('TerminalTaskSystem.noBuildTask', 'No build task defined in tasks.json'), TaskErrors.NoBuildTask);
+ }
+ return this.run(this.buildTaskIdentifier, Triggers.shortcut);
+ }
+
+ public rebuild(): ITaskExecuteResult {
+ throw new Error('Task - Rebuild: not implemented yet');
+ }
+
+ public clean(): ITaskExecuteResult {
+ throw new Error('Task - Clean: not implemented yet');
+ }
+
+ public runTest(): ITaskExecuteResult {
+ if (!this.testTaskIdentifier) {
+ throw new TaskError(Severity.Info, nls.localize('TerminalTaskSystem.noTestTask', 'No test task defined in tasks.json'), TaskErrors.NoTestTask);
+ }
+ return this.run(this.testTaskIdentifier, Triggers.shortcut);
+ }
+
+ public run(taskIdentifier: string, trigger: string = Triggers.command): ITaskExecuteResult {
+ let task = this.configuration.tasks[taskIdentifier];
+ if (!task) {
+ throw new TaskError(Severity.Info, nls.localize('TerminalTaskSystem.noTask', 'Task \'{0}\' not found', taskIdentifier), TaskErrors.TaskNotFound);
+ }
+ let telemetryEvent: TelemetryEvent = {
+ trigger: trigger,
+ command: 'other',
+ success: true
+ };
+ try {
+ let result = this.executeTask(task, telemetryEvent);
+ result.promise = result.promise.then((summary) => {
+ this.telemetryService.publicLog(TerminalTaskSystem.TelemetryEventName, telemetryEvent);
+ return summary;
+ }, (error) => {
+ telemetryEvent.success = false;
+ this.telemetryService.publicLog(TerminalTaskSystem.TelemetryEventName, telemetryEvent);
+ return TPromise.wrapError(error);
+ });
+ return result;
+ } catch (error) {
+ telemetryEvent.success = false;
+ this.telemetryService.publicLog(TerminalTaskSystem.TelemetryEventName, telemetryEvent);
+ if (error instanceof TaskError) {
+ throw error;
+ } else if (error instanceof Error) {
+ this.log(error.message);
+ throw new TaskError(Severity.Error, error.message, TaskErrors.UnknownError);
+ } else {
+ this.log(error.toString());
+ throw new TaskError(Severity.Error, nls.localize('TerminalTaskSystem.unknownError', 'A unknown error has occurred while executing a task. See task output log for details.'), TaskErrors.UnknownError);
+ }
+ }
+ }
+
+ public isActive(): TPromise {
+ return TPromise.as(this.isActiveSync());
+ }
+
+ public isActiveSync(): boolean {
+ return Object.keys(this.activeTasks).length > 0;
+ }
+
+ public canAutoTerminate(): boolean {
+ return Object.keys(this.activeTasks).every(key => this.configuration.tasks[key].isBackground);
+ }
+
+ public terminate(): TPromise {
+ Object.keys(this.activeTasks).forEach((key) => {
+ let data = this.activeTasks[key];
+ data.terminal.dispose();
+ });
+ this.activeTasks = Object.create(null);
+ return TPromise.as({ success: true });
+ }
+
+ public tasks(): TPromise {
+ let result: TaskDescription[];
+ if (!this.configuration || !this.configuration.tasks) {
+ result = [];
+ } else {
+ result = Object.keys(this.configuration.tasks).map(key => this.configuration.tasks[key]);
+ }
+ return TPromise.as(result);
+ }
+
+ private executeTask(task: TaskDescription, telemetryEvent: TelemetryEvent): ITaskExecuteResult {
+ let terminalData = this.activeTasks[task.id];
+ if (terminalData && terminalData.promise) {
+ if (task.showOutput === ShowOutput.Always) {
+ terminalData.terminal.setVisible(true);
+ }
+ return { kind: TaskExecuteKind.Active, active: { same: true, background: task.isBackground }, promise: terminalData.promise };
+ } else {
+ let terminal: ITerminalInstance = undefined;
+ let promise: TPromise = undefined;
+ if (task.isBackground) {
+ promise = new TPromise((resolve, reject) => {
+ let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService);
+ let toUnbind: IDisposable[] = [];
+ let event: TaskEvent = { taskId: task.id, taskName: task.name, type: TaskType.Watching };
+ let eventCounter: number = 0;
+ toUnbind.push(watchingProblemMatcher.addListener2(ProblemCollectorEvents.WatchingBeginDetected, () => {
+ eventCounter++;
+ this.emit(TaskSystemEvents.Active, event);
+ }));
+ toUnbind.push(watchingProblemMatcher.addListener2(ProblemCollectorEvents.WatchingEndDetected, () => {
+ eventCounter--;
+ this.emit(TaskSystemEvents.Inactive, event);
+ }));
+ watchingProblemMatcher.aboutToStart();
+ let delayer: Async.Delayer = null;
+ let decoder = new TerminalDecoder();
+ terminal = this.createTerminal(task);
+ terminal.onData((data: string) => {
+ decoder.write(data).forEach(line => {
+ watchingProblemMatcher.processLine(line);
+ if (delayer === null) {
+ delayer = new Async.Delayer(3000);
+ }
+ delayer.trigger(() => {
+ watchingProblemMatcher.forceDelivery();
+ delayer = null;
+ });
+ });
+ });
+ terminal.onExit((exitCode) => {
+ watchingProblemMatcher.dispose();
+ toUnbind = dispose(toUnbind);
+ toUnbind = null;
+ for (let i = 0; i < eventCounter; i++) {
+ this.emit(TaskSystemEvents.Inactive, event);
+ }
+ eventCounter = 0;
+ if (exitCode && exitCode === 1 && watchingProblemMatcher.numberOfMatches === 0 && task.showOutput !== ShowOutput.Never) {
+ this.terminalService.setActiveInstance(terminal);
+ this.terminalService.showPanel(false);
+ }
+ resolve({ exitCode });
+ });
+ });
+ } else {
+ promise = new TPromise((resolve, reject) => {
+ terminal = this.createTerminal(task);
+ this.emit(TaskSystemEvents.Active, event);
+ let decoder = new TerminalDecoder();
+ let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService);
+ terminal.onData((data: string) => {
+ decoder.write(data).forEach((line) => {
+ startStopProblemMatcher.processLine(line);
+ });
+ });
+ terminal.onExit((exitCode) => {
+ startStopProblemMatcher.processLine(decoder.end());
+ startStopProblemMatcher.done();
+ startStopProblemMatcher.dispose();
+ this.emit(TaskSystemEvents.Inactive, event);
+ delete this.activeTasks[task.id];
+ resolve({ exitCode });
+ });
+ this.terminalService.setActiveInstance(terminal);
+ if (task.showOutput === ShowOutput.Always) {
+ this.terminalService.showPanel(false);
+ }
+ });
+ }
+ this.terminalService.setActiveInstance(terminal);
+ if (task.showOutput === ShowOutput.Always) {
+ this.terminalService.showPanel(false);
+ }
+ this.activeTasks[task.id] = { terminal, promise };
+ return { kind: TaskExecuteKind.Started, started: {}, promise: promise };
+ }
+ }
+
+ private createTerminal(task: TaskDescription): ITerminalInstance {
+ let { command, args } = this.resolveCommandAndArgs(task);
+ let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', task.name);
+ let waitOnExit = task.showOutput !== ShowOutput.Never || !task.isBackground;
+ if (this.configuration.isShellCommand) {
+ // TODO@dirk: don't we want to use cmd.exe (32- or 64-bit) all the time? Also you can now
+ // not set IShellLaunchConfig.executable which will grab it from settings.
+ let shellConfig: IShellLaunchConfig = { executable: null, args: null };
+ (this.terminalService.configHelper as TerminalConfigHelper).mergeDefaultShellPathAndArgs(shellConfig);
+ let shellArgs = shellConfig.args.slice(0);
+ let toAdd: string[] = [];
+ let commandLine: string;
+ if (Platform.isWindows) {
+ toAdd.push('/d', '/c');
+ let quotedCommand: boolean = false;
+ let quotedArg: boolean = false;
+ let quoted = this.ensureDoubleQuotes(command);
+ let commandPieces: string[] = [];
+ commandPieces.push(quoted.value);
+ quotedCommand = quoted.quoted;
+ if (args) {
+ args.forEach((arg) => {
+ quoted = this.ensureDoubleQuotes(arg);
+ commandPieces.push(quoted.value);
+ quotedArg = quotedArg && quoted.quoted;
+ });
+ }
+ if (quotedCommand) {
+ if (quotedArg) {
+ commandLine = '"' + commandPieces.join(' ') + '"';
+ } else {
+ if (commandPieces.length > 1) {
+ commandLine = '"' + commandPieces[0] + '"' + ' ' + commandPieces.slice(1).join(' ');
+ } else {
+ commandLine = '"' + commandPieces[0] + '"';
+ }
+ }
+ } else {
+ commandLine = commandPieces.join(' ');
+ }
+ } else {
+ toAdd.push('-c');
+ commandLine = `${command} ${args.join(' ')}`;
+ }
+ toAdd.forEach(element => {
+ if (!shellArgs.some(arg => arg.toLowerCase() === element)) {
+ shellArgs.push(element);
+ }
+ });
+ shellArgs.push(commandLine);
+ const shellLaunchConfig: IShellLaunchConfig = {
+ name: terminalName,
+ executable: shellConfig.executable,
+ args: shellArgs,
+ waitOnExit
+ };
+ return this.terminalService.createInstance(shellLaunchConfig);
+ } else {
+ const shellLaunchConfig: IShellLaunchConfig = {
+ name: terminalName,
+ executable: command,
+ args,
+ waitOnExit
+ };
+ return this.terminalService.createInstance(shellLaunchConfig);
+ }
+ }
+
+ private resolveCommandAndArgs(task: TaskDescription): { command: string, args: string[] } {
+ let args: string[] = this.configuration.args ? this.configuration.args.slice() : [];
+ // We need to first pass the task name
+ if (!task.suppressTaskName) {
+ if (this.fileConfig.taskSelector) {
+ args.push(this.fileConfig.taskSelector + task.name);
+ } else {
+ args.push(task.name);
+ }
+ }
+ // And then additional arguments
+ if (task.args) {
+ args = args.concat(task.args);
+ }
+ args = this.resolveVariables(args);
+ let command: string = this.resolveVariable(this.configuration.command);
+ return { command, args };
+ }
+
+
+ private resolveVariables(value: string[]): string[] {
+ return value.map(s => this.resolveVariable(s));
+ }
+
+ private resolveMatchers(values: T[]): T[] {
+ if (values.length === 0) {
+ return values;
+ }
+ let result: T[] = [];
+ values.forEach((matcher) => {
+ if (!matcher.filePrefix) {
+ result.push(matcher);
+ } else {
+ let copy = Objects.clone(matcher);
+ copy.filePrefix = this.resolveVariable(copy.filePrefix);
+ result.push(copy);
+ }
+ });
+ return result;
+ }
+
+ private resolveVariable(value: string): string {
+ return this.configurationResolverService.resolve(value);
+ }
+
+ private static doubleQuotes = /^[^"].* .*[^"]$/;
+ private ensureDoubleQuotes(value: string) {
+ if (TerminalTaskSystem.doubleQuotes.test(value)) {
+ return {
+ value: '"' + value + '"',
+ quoted: true
+ };
+ } else {
+ return {
+ value: value,
+ quoted: value.length > 0 && value[0] === '"' && value[value.length - 1] === '"'
+ };
+ }
+ }
+
+ private static WellKnowCommands: IStringDictionary = {
+ 'ant': true,
+ 'cmake': true,
+ 'eslint': true,
+ 'gradle': true,
+ 'grunt': true,
+ 'gulp': true,
+ 'jake': true,
+ 'jenkins': true,
+ 'jshint': true,
+ 'make': true,
+ 'maven': true,
+ 'msbuild': true,
+ 'msc': true,
+ 'nmake': true,
+ 'npm': true,
+ 'rake': true,
+ 'tsc': true,
+ 'xbuild': true
+ };
+ public getSanitizedCommand(cmd: string): string {
+ let result = cmd.toLowerCase();
+ let index = result.lastIndexOf(path.sep);
+ if (index !== -1) {
+ result = result.substring(index + 1);
+ }
+ if (TerminalTaskSystem.WellKnowCommands[result]) {
+ return result;
+ }
+ return 'other';
+ }
+}
\ No newline at end of file
diff --git a/src/vs/workbench/parts/tasks/node/processRunnerConfiguration.ts b/src/vs/workbench/parts/tasks/node/processRunnerConfiguration.ts
index 54ed1f79ee5..6634b8455fe 100644
--- a/src/vs/workbench/parts/tasks/node/processRunnerConfiguration.ts
+++ b/src/vs/workbench/parts/tasks/node/processRunnerConfiguration.ts
@@ -451,7 +451,7 @@ class ConfigurationParser {
name: globals.command,
showOutput: globals.showOutput,
suppressTaskName: true,
- isWatching: isWatching,
+ isBackground: isWatching,
promptOnClose: promptOnClose,
echoCommand: globals.echoCommand,
};
@@ -539,15 +539,15 @@ class ConfigurationParser {
if (Types.isStringArray(externalTask.args)) {
task.args = externalTask.args.slice();
}
- task.isWatching = false;
+ task.isBackground = false;
if (!Types.isUndefined(externalTask.isWatching)) {
- task.isWatching = !!externalTask.isWatching;
+ task.isBackground = !!externalTask.isWatching;
}
task.promptOnClose = true;
if (!Types.isUndefined(externalTask.promptOnClose)) {
task.promptOnClose = !!externalTask.promptOnClose;
} else {
- task.promptOnClose = !task.isWatching;
+ task.promptOnClose = !task.isBackground;
}
if (Types.isString(externalTask.showOutput)) {
task.showOutput = TaskSystem.ShowOutput.fromString(externalTask.showOutput);
diff --git a/src/vs/workbench/parts/tasks/node/processRunnerSystem.ts b/src/vs/workbench/parts/tasks/node/processRunnerSystem.ts
index 9a90c03d842..643d5bcc4b5 100644
--- a/src/vs/workbench/parts/tasks/node/processRunnerSystem.ts
+++ b/src/vs/workbench/parts/tasks/node/processRunnerSystem.ts
@@ -90,7 +90,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
public build(): ITaskExecuteResult {
if (this.activeTaskIdentifier) {
let task = this.configuration.tasks[this.activeTaskIdentifier];
- return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === this.defaultBuildTaskIdentifier, watching: task.isWatching }, promise: this.activeTaskPromise };
+ return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === this.defaultBuildTaskIdentifier, background: task.isBackground }, promise: this.activeTaskPromise };
}
if (!this.defaultBuildTaskIdentifier) {
throw new TaskError(Severity.Info, nls.localize('TaskRunnerSystem.noBuildTask', 'No task is marked as a build task in the tasks.json. Mark a task with \'isBuildCommand\'.'), TaskErrors.NoBuildTask);
@@ -109,7 +109,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
public runTest(): ITaskExecuteResult {
if (this.activeTaskIdentifier) {
let task = this.configuration.tasks[this.activeTaskIdentifier];
- return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === this.defaultTestTaskIdentifier, watching: task.isWatching }, promise: this.activeTaskPromise };
+ return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === this.defaultTestTaskIdentifier, background: task.isBackground }, promise: this.activeTaskPromise };
}
if (!this.defaultTestTaskIdentifier) {
throw new TaskError(Severity.Info, nls.localize('TaskRunnerSystem.noTestTask', 'No test task configured.'), TaskErrors.NoTestTask);
@@ -120,7 +120,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
public run(taskIdentifier: string): ITaskExecuteResult {
if (this.activeTaskIdentifier) {
let task = this.configuration.tasks[this.activeTaskIdentifier];
- return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === taskIdentifier, watching: task.isWatching }, promise: this.activeTaskPromise };
+ return { kind: TaskExecuteKind.Active, active: { same: this.activeTaskIdentifier === taskIdentifier, background: task.isBackground }, promise: this.activeTaskPromise };
}
return this.executeTask(taskIdentifier);
}
@@ -239,7 +239,7 @@ export class ProcessRunnerSystem extends EventEmitter implements ITaskSystem {
let prompt: string = Platform.isWindows ? '>' : '$';
this.log(`running command${prompt} ${command} ${args.join(' ')}`);
}
- if (task.isWatching) {
+ if (task.isBackground) {
let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService);
let toUnbind: IDisposable[] = [];
let event: TaskEvent = { taskId: task.id, taskName: task.name, type: TaskType.Watching };
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 2cd2709a8d8..4b83891a7b0 100644
--- a/src/vs/workbench/parts/tasks/test/node/configuration.test.ts
+++ b/src/vs/workbench/parts/tasks/test/node/configuration.test.ts
@@ -73,7 +73,7 @@ class TaskBuilder {
showOutput: TaskSystem.ShowOutput.Always,
suppressTaskName: false,
echoCommand: false,
- isWatching: false,
+ isBackground: false,
promptOnClose: true,
problemMatchers: []
};
@@ -99,8 +99,8 @@ class TaskBuilder {
return this;
}
- public isWatching(value: boolean): TaskBuilder {
- this.result.isWatching = value;
+ public isBackground(value: boolean): TaskBuilder {
+ this.result.isBackground = value;
return this;
}
@@ -307,7 +307,7 @@ suite('Tasks Configuration parsing tests', () => {
let builder = new ConfiguationBuilder('tsc');
builder.task('tsc').
suppressTaskName(true).
- isWatching(true).
+ isBackground(true).
promptOnClose(false);
testGobalCommand(
{
@@ -627,7 +627,7 @@ suite('Tasks Configuration parsing tests', () => {
showOutput(TaskSystem.ShowOutput.Never).
echoCommand(true).
args(['--p']).
- isWatching(true).
+ isBackground(true).
promptOnClose(false);
let result = testConfiguration(external, builder);
@@ -833,7 +833,7 @@ suite('Tasks Configuration parsing tests', () => {
]
};
let builder = new ConfiguationBuilder('tsc');
- builder.task('taskName').isWatching(true).promptOnClose(false);
+ builder.task('taskName').isBackground(true).promptOnClose(false);
testConfiguration(external, builder);
});
@@ -943,7 +943,7 @@ suite('Tasks Configuration parsing tests', () => {
assert.strictEqual(actual.showOutput, expected.showOutput, 'showOutput');
assert.strictEqual(actual.suppressTaskName, expected.suppressTaskName, 'suppressTaskName');
assert.strictEqual(actual.echoCommand, expected.echoCommand, 'echoCommand');
- assert.strictEqual(actual.isWatching, expected.isWatching, 'isWatching');
+ assert.strictEqual(actual.isBackground, expected.isBackground, 'isBackground');
assert.strictEqual(actual.promptOnClose, expected.promptOnClose, 'promptOnClose');
assert.strictEqual(typeof actual.problemMatchers, typeof expected.problemMatchers);
if (actual.problemMatchers && expected.problemMatchers) {
diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts
index e03abc7ba4d..aaff277951a 100644
--- a/src/vs/workbench/parts/terminal/common/terminal.ts
+++ b/src/vs/workbench/parts/terminal/common/terminal.ts
@@ -6,7 +6,6 @@
import Event from 'vs/base/common/event';
import platform = require('vs/base/common/platform');
-import processes = require('vs/base/node/processes');
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
@@ -17,7 +16,6 @@ export const TERMINAL_SERVICE_ID = 'terminalService';
export const TERMINAL_DEFAULT_SHELL_LINUX = !platform.isWindows ? (process.env.SHELL || 'sh') : 'sh';
export const TERMINAL_DEFAULT_SHELL_OSX = !platform.isWindows ? (process.env.SHELL || 'sh') : 'sh';
-export const TERMINAL_DEFAULT_SHELL_WINDOWS = processes.getWindowsShell();
export const TERMINAL_DEFAULT_RIGHT_CLICK_COPY_PASTE = platform.isWindows;
@@ -80,11 +78,28 @@ export interface ITerminalFont {
}
export interface IShellLaunchConfig {
- executable: string;
- args: string[];
- /** Whether to ignore a custom cwd (if the shell is being launched by an extension) */
- ignoreCustomCwd?: boolean;
- /** Whether to wait for a key press before closing the terminal */
+ /** The name of the terminal, this this is not set the name of the process will be used. */
+ name?: string;
+ /** The shell executable (bash, cmd, etc.). */
+ executable?: string;
+ /** The CLI arguments to use with executable. */
+ args?: string[];
+ /**
+ * The current working directory of the terminal, this overrides the `terminal.integrated.cwd`
+ * settings key.
+ */
+ cwd?: string;
+ /**
+ * A custom environment for the terminal, if this is not set the environment will be inherited
+ * from the VS Code process.
+ */
+ env?: { [key: string]: string };
+ /**
+ * Whether to ignore a custom cwd from the `terminal.integrated.cwd` settings key (eg. if the
+ * shell is being launched by an extension).
+ */
+ ignoreConfigurationCwd?: boolean;
+ /** Whether to wait for a key press before closing the terminal. */
waitOnExit?: boolean;
}
@@ -100,7 +115,7 @@ export interface ITerminalService {
onInstanceTitleChanged: Event;
terminalInstances: ITerminalInstance[];
- createInstance(name?: string, shellPath?: string, shellArgs?: string[], waitOnExit?: boolean, ignoreCustomCwd?: boolean): ITerminalInstance;
+ createInstance(shell?: IShellLaunchConfig): ITerminalInstance;
getInstanceFromId(terminalId: number): ITerminalInstance;
getInstanceLabels(): string[];
getActiveInstance(): ITerminalInstance;
diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css
index 031c18fbda5..5fbbe5a1485 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css
+++ b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css
@@ -40,7 +40,7 @@
.monaco-workbench .panel.integrated-terminal .xterm-viewport {
/* Align the viewport to the bottom of the panel, just like the terminal */
position: absolute;
- right: 0;
+ right: -20px;
bottom: 0;
left: 0;
}
diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css b/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css
index f6c97a0c0b4..1f92ef33775 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css
+++ b/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css
@@ -82,14 +82,14 @@
outline-offset: -1px;
}
-.monaco-workbench .panel.integrated-terminal .xterm.focus .terminal-cursor.blinking,
-.monaco-workbench .panel.integrated-terminal .xterm:focus .terminal-cursor.blinking { animation: blink-cursor 1.2s infinite step-end; }
-.vs-dark .monaco-workbench .panel.integrated-terminal .xterm.focus .terminal-cursor.blinking,
-.vs-dark .monaco-workbench .panel.integrated-terminal .xterm:focus .terminal-cursor.blinking { animation: blink-cursor-dark 1.2s infinite step-end; }
-.hc-black .monaco-workbench .panel.integrated-terminal .xterm.focus .terminal-cursor.blinking,
-.hc-black .monaco-workbench .panel.integrated-terminal .xterm:focus .terminal-cursor.blinking { animation: blink-cursor-hc-black 1.2s infinite step-end; }
+.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-blink.focus .terminal-cursor,
+.monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-blink:focus .terminal-cursor { animation: cursor-blink 1.2s infinite step-end; }
+.vs-dark .monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-blink.focus .terminal-cursor,
+.vs-dark .monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-blink:focus .terminal-cursor { animation: cursor-blink-dark 1.2s infinite step-end; }
+.hc-black .monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-blink.focus .terminal-cursor,
+.hc-black .monaco-workbench .panel.integrated-terminal .xterm.xterm-cursor-blink:focus .terminal-cursor { animation: cursor-blink-hc-black 1.2s infinite step-end; }
-@keyframes blink-cursor {
+@keyframes cursor-blink {
0% {
background-color: #333;
color: #CCC;
@@ -100,7 +100,7 @@
}
}
-@keyframes blink-cursor-dark {
+@keyframes cursor-blink-dark {
0% {
background-color: #CCC;
color: #1e1e1e;
@@ -111,7 +111,7 @@
}
}
-@keyframes blink-cursor-hc-black {
+@keyframes cursor-blink-hc-black {
0% {
background-color: #fff;
color: #000;
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts
index f4fee11ede3..f2993220872 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts
@@ -11,7 +11,8 @@ import * as platform from 'vs/base/common/platform';
import nls = require('vs/nls');
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { GlobalQuickOpenAction } from 'vs/workbench/browser/parts/quickopen/quickopen.contribution';
-import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, TERMINAL_DEFAULT_SHELL_LINUX, TERMINAL_DEFAULT_SHELL_OSX, TERMINAL_DEFAULT_SHELL_WINDOWS, TERMINAL_DEFAULT_RIGHT_CLICK_COPY_PASTE } from 'vs/workbench/parts/terminal/common/terminal';
+import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, TERMINAL_DEFAULT_SHELL_LINUX, TERMINAL_DEFAULT_SHELL_OSX, TERMINAL_DEFAULT_RIGHT_CLICK_COPY_PASTE } from 'vs/workbench/parts/terminal/common/terminal';
+import { TERMINAL_DEFAULT_SHELL_WINDOWS } from 'vs/workbench/parts/terminal/electron-browser/terminal';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.ts
new file mode 100644
index 00000000000..8b19744bc10
--- /dev/null
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.ts
@@ -0,0 +1,18 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 os from 'os';
+import platform = require('vs/base/common/platform');
+import processes = require('vs/base/node/processes');
+
+const powerShellExePath =
+ !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')
+ ? `${process.env.windir}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
+ : `${process.env.windir}\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe`;
+
+const isAtLeastWindows10 = platform.isWindows && parseFloat(os.release()) >= 10;
+
+export const TERMINAL_DEFAULT_SHELL_WINDOWS = isAtLeastWindows10 ? powerShellExePath : processes.getWindowsShell();
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts
index e729c45dada..40fc991dbdf 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts
@@ -152,12 +152,12 @@ export class TerminalConfigHelper implements ITerminalConfigHelper {
return config.terminal.integrated.commandsToSkipShell;
}
- public getShell(): IShellLaunchConfig {
+ public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig): IShellLaunchConfig {
const config = this._configurationService.getConfiguration();
- const shell: IShellLaunchConfig = {
- executable: '',
- args: []
- };
+
+ shell.executable = '';
+ shell.args = [];
+
const integrated = config && config.terminal && config.terminal.integrated;
if (integrated && integrated.shell && integrated.shellArgs) {
if (this._platform === Platform.Windows) {
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts
index de119ecdc7f..97eb1459215 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts
@@ -63,7 +63,6 @@ export class TerminalInstance implements ITerminalInstance {
private _terminalFocusContextKey: IContextKey,
private _configHelper: TerminalConfigHelper,
private _container: HTMLElement,
- name: string,
private _shellLaunchConfig: IShellLaunchConfig,
@IContextKeyService private _contextKeyService: IContextKeyService,
@IKeybindingService private _keybindingService: IKeybindingService,
@@ -84,7 +83,7 @@ export class TerminalInstance implements ITerminalInstance {
this._onProcessIdReady = new Emitter();
this._onTitleChanged = new Emitter();
- this._createProcess(this._contextService.getWorkspace(), name, this._shellLaunchConfig);
+ this._createProcess(this._contextService.getWorkspace(), this._shellLaunchConfig);
if (_container) {
this.attachToElement(_container);
@@ -105,7 +104,9 @@ export class TerminalInstance implements ITerminalInstance {
DOM.addClass(this._wrapperElement, 'terminal-wrapper');
this._xtermElement = document.createElement('div');
- this._xterm = xterm();
+ this._xterm = xterm({
+ scrollback: this._configHelper.getScrollback()
+ });
this._xterm.open(this._xtermElement);
this._process.on('message', (message) => {
@@ -309,11 +310,15 @@ export class TerminalInstance implements ITerminalInstance {
return typeof data === 'string' ? data.replace(TerminalInstance.EOL_REGEX, os.EOL) : data;
}
- protected _getCwd(workspace: IWorkspace, ignoreCustomCwd: boolean): string {
+ protected _getCwd(shell: IShellLaunchConfig, workspace: IWorkspace): string {
+ if (shell.cwd) {
+ return shell.cwd;
+ }
+
let cwd: string;
// TODO: Handle non-existent customCwd
- if (!ignoreCustomCwd) {
+ if (!shell.ignoreConfigurationCwd) {
// Evaluate custom cwd first
const customCwd = this._configHelper.getCwd();
if (customCwd) {
@@ -333,18 +338,18 @@ export class TerminalInstance implements ITerminalInstance {
return TerminalInstance._sanitizeCwd(cwd);
}
- protected _createProcess(workspace: IWorkspace, name: string, shell: IShellLaunchConfig) {
+ protected _createProcess(workspace: IWorkspace, shell: IShellLaunchConfig) {
const locale = this._configHelper.isSetLocaleVariables() ? platform.locale : undefined;
if (!shell.executable) {
- shell = this._configHelper.getShell();
+ this._configHelper.mergeDefaultShellPathAndArgs(shell);
}
- const env = TerminalInstance.createTerminalEnv(process.env, shell, this._getCwd(workspace, shell.ignoreCustomCwd), locale);
- this._title = name ? name : '';
+ const env = TerminalInstance.createTerminalEnv(process.env, shell, this._getCwd(shell, workspace), locale);
+ this._title = shell.name || '';
this._process = cp.fork('./terminalProcess', [], {
env: env,
cwd: URI.parse(path.dirname(require.toUrl('./terminalProcess'))).fsPath
});
- if (!name) {
+ if (!shell.name) {
// Only listen for process title changes when a name is not provided
this._process.on('message', (message) => {
if (message.type === 'title') {
@@ -377,7 +382,11 @@ export class TerminalInstance implements ITerminalInstance {
exitCodeMessage = nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode);
}
- if (this._shellLaunchConfig.waitOnExit) {
+ // Only trigger wait on exit when the exit was triggered by the process, not through the
+ // `workbench.action.terminal.kill` command
+ const triggeredByProcess = exitCode !== null;
+
+ if (triggeredByProcess && this._shellLaunchConfig.waitOnExit) {
if (exitCode) {
this._xterm.writeln(exitCodeMessage);
}
@@ -411,7 +420,7 @@ export class TerminalInstance implements ITerminalInstance {
// TODO: This should be private/protected
// TODO: locale should not be optional
public static createTerminalEnv(parentEnv: IStringDictionary, shell: IShellLaunchConfig, cwd: string, locale?: string): IStringDictionary {
- const env = TerminalInstance._cloneEnv(parentEnv);
+ const env = shell.env ? shell.env : TerminalInstance._cloneEnv(parentEnv);
env['PTYPID'] = process.pid.toString();
env['PTYSHELL'] = shell.executable;
if (shell.args) {
@@ -482,6 +491,7 @@ export class TerminalInstance implements ITerminalInstance {
private _setScrollback(lineCount: number): void {
if (this._xterm && this._xterm.getOption('scrollback') !== lineCount) {
+ console.log('set scrollback to: ' + lineCount);
this._xterm.setOption('scrollback', lineCount);
}
}
diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts
index da6adf7cca8..c7b494dc79f 100644
--- a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts
+++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts
@@ -61,18 +61,11 @@ export class TerminalService implements ITerminalService {
this.onInstanceDisposed((terminalInstance) => { this._removeInstance(terminalInstance); });
}
- public createInstance(name?: string, shellPath?: string, shellArgs?: string[], waitOnExit?: boolean, ignoreCustomCwd?: boolean): ITerminalInstance {
- let shell: IShellLaunchConfig = {
- executable: shellPath,
- args: shellArgs,
- waitOnExit,
- ignoreCustomCwd
- };
+ public createInstance(shell: IShellLaunchConfig = {}): ITerminalInstance {
let terminalInstance = this._instantiationService.createInstance(TerminalInstance,
this._terminalFocusContextKey,
this._configHelper,
this._terminalContainer,
- name,
shell);
terminalInstance.addDisposable(terminalInstance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged));
terminalInstance.addDisposable(terminalInstance.onClosed(this._onInstanceDisposed.fire, this._onInstanceDisposed));
diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts
index 7a71a7777db..ae338b2f906 100644
--- a/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts
+++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts
@@ -10,6 +10,7 @@ import { IConfigurationService, getConfigurationValue } from 'vs/platform/config
import { Platform } from 'vs/base/common/platform';
import { TPromise } from 'vs/base/common/winjs.base';
import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
+import { IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
import { DefaultConfig } from 'vs/editor/common/config/defaultConfig';
@@ -156,6 +157,7 @@ suite('Workbench - TerminalConfigHelper', () => {
test('TerminalConfigHelper - getShell', function () {
let configurationService: IConfigurationService;
let configHelper: TerminalConfigHelper;
+ let shellConfig: IShellLaunchConfig;
configurationService = new MockConfigurationService({
terminal: {
@@ -171,7 +173,9 @@ suite('Workbench - TerminalConfigHelper', () => {
});
configHelper = new TerminalConfigHelper(Platform.Linux, configurationService);
configHelper.panelContainer = fixture;
- assert.equal(configHelper.getShell().executable, 'foo', 'terminal.integrated.shell.linux should be selected on Linux');
+ shellConfig = { executable: null, args: [] };
+ configHelper.mergeDefaultShellPathAndArgs(shellConfig);
+ assert.equal(shellConfig.executable, 'foo', 'terminal.integrated.shell.linux should be selected on Linux');
configurationService = new MockConfigurationService({
terminal: {
@@ -187,7 +191,9 @@ suite('Workbench - TerminalConfigHelper', () => {
});
configHelper = new TerminalConfigHelper(Platform.Mac, configurationService);
configHelper.panelContainer = fixture;
- assert.equal(configHelper.getShell().executable, 'foo', 'terminal.integrated.shell.osx should be selected on OS X');
+ shellConfig = { executable: null, args: [] };
+ configHelper.mergeDefaultShellPathAndArgs(shellConfig);
+ assert.equal(shellConfig.executable, 'foo', 'terminal.integrated.shell.osx should be selected on OS X');
configurationService = new MockConfigurationService({
terminal: {
@@ -203,7 +209,9 @@ suite('Workbench - TerminalConfigHelper', () => {
});
configHelper = new TerminalConfigHelper(Platform.Windows, configurationService);
configHelper.panelContainer = fixture;
- assert.equal(configHelper.getShell().executable, 'foo', 'terminal.integrated.shell.windows should be selected on Windows');
+ shellConfig = { executable: null, args: [] };
+ configHelper.mergeDefaultShellPathAndArgs(shellConfig);
+ assert.equal(shellConfig.executable, 'foo', 'terminal.integrated.shell.windows should be selected on Windows');
});
test('TerminalConfigHelper - getTheme', function () {
diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalInstance.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalInstance.test.ts
index 29dbc9fdd7c..ddd67e93ef9 100644
--- a/src/vs/workbench/parts/terminal/test/electron-browser/terminalInstance.test.ts
+++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalInstance.test.ts
@@ -20,11 +20,11 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
class TestTerminalInstance extends TerminalInstance {
- public _getCwd(workspace: IWorkspace, ignoreCustomCwd: boolean): string {
- return super._getCwd(workspace, ignoreCustomCwd);
+ public _getCwd(shell: IShellLaunchConfig, workspace: IWorkspace): string {
+ return super._getCwd(shell, workspace);
}
- protected _createProcess(workspace: IWorkspace, name: string, shell: IShellLaunchConfig): void { }
+ protected _createProcess(workspace: IWorkspace, shell: IShellLaunchConfig): void { }
}
suite('Workbench - TerminalInstance', () => {
@@ -91,7 +91,7 @@ suite('Workbench - TerminalInstance', () => {
configHelper = {
getCwd: () => null
};
- instance = instantiationService.createInstance(TestTerminalInstance, terminalFocusContextKey, configHelper, null, null, null);
+ instance = instantiationService.createInstance(TestTerminalInstance, terminalFocusContextKey, configHelper, null, null);
});
// This helper checks the paths in a cross-platform friendly manner
@@ -100,39 +100,39 @@ suite('Workbench - TerminalInstance', () => {
}
test('should default to os.homedir() for an empty workspace', () => {
- assertPathsMatch(instance._getCwd(null, false), os.homedir());
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), os.homedir());
});
test('should use to the workspace if it exists', () => {
- assertPathsMatch(instance._getCwd({ resource: Uri.file('/foo') }, false), '/foo');
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, { resource: Uri.file('/foo') }), '/foo');
});
test('should use an absolute custom cwd as is', () => {
configHelper.getCwd = () => '/foo';
- assertPathsMatch(instance._getCwd(null, false), '/foo');
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), '/foo');
});
test('should normalize a relative custom cwd against the workspace path', () => {
configHelper.getCwd = () => 'foo';
- assertPathsMatch(instance._getCwd({ resource: Uri.file('/bar') }, false), '/bar/foo');
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, { resource: Uri.file('/bar') }), '/bar/foo');
configHelper.getCwd = () => './foo';
- assertPathsMatch(instance._getCwd({ resource: Uri.file('/bar') }, false), '/bar/foo');
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, { resource: Uri.file('/bar') }), '/bar/foo');
configHelper.getCwd = () => '../foo';
- assertPathsMatch(instance._getCwd({ resource: Uri.file('/bar') }, false), '/foo');
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, { resource: Uri.file('/bar') }, ), '/foo');
});
test('should fall back for relative a custom cwd that doesn\'t have a workspace', () => {
configHelper.getCwd = () => 'foo';
- assertPathsMatch(instance._getCwd(null, false), os.homedir());
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), os.homedir());
configHelper.getCwd = () => './foo';
- assertPathsMatch(instance._getCwd(null, false), os.homedir());
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), os.homedir());
configHelper.getCwd = () => '../foo';
- assertPathsMatch(instance._getCwd(null, false), os.homedir());
+ assertPathsMatch(instance._getCwd({ executable: null, args: [] }, null), os.homedir());
});
test('should ignore custom cwd when told to ignore', () => {
configHelper.getCwd = () => '/foo';
- assertPathsMatch(instance._getCwd({ resource: Uri.file('/bar') }, true), '/bar');
+ assertPathsMatch(instance._getCwd({ executable: null, args: [], ignoreConfigurationCwd: true }, { resource: Uri.file('/bar') }), '/bar');
});
});
});
\ No newline at end of file
diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalService.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalService.test.ts
index bbd6384ee8e..b88447bd5f2 100644
--- a/src/vs/workbench/parts/terminal/test/electron-browser/terminalService.test.ts
+++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalService.test.ts
@@ -5,14 +5,13 @@
'use strict';
-//import * as assert from 'assert';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
-//import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/terminalInstance';
import { TerminalService } from 'vs/workbench/parts/terminal/electron-browser/terminalService';
-import { TERMINAL_DEFAULT_SHELL_LINUX, TERMINAL_DEFAULT_SHELL_OSX, TERMINAL_DEFAULT_SHELL_WINDOWS } from 'vs/workbench/parts/terminal/common/terminal';
+import { TERMINAL_DEFAULT_SHELL_LINUX, TERMINAL_DEFAULT_SHELL_OSX } from 'vs/workbench/parts/terminal/common/terminal';
+import { TERMINAL_DEFAULT_SHELL_WINDOWS } from 'vs/workbench/parts/terminal/electron-browser/terminal';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TPromise } from 'vs/base/common/winjs.base';
diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts
index 60dd1fab10e..cd616e9cb83 100644
--- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts
+++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts
@@ -21,7 +21,8 @@ import { IKeybindingEvent, IKeybindingItem, IUserFriendlyKeybinding, KeybindingS
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IKeybindingRule, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Registry } from 'vs/platform/platform';
-import { ITelemetryService, keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
import { getNativeLabelProvider, getNativeAriaLabelProvider } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymap';
import { IMessageService } from 'vs/platform/message/common/message';
import { ConfigWatcher } from 'vs/base/node/config';
diff --git a/src/vs/workbench/services/mode/common/workbenchModeService.ts b/src/vs/workbench/services/mode/common/workbenchModeService.ts
new file mode 100644
index 00000000000..fc65de33c91
--- /dev/null
+++ b/src/vs/workbench/services/mode/common/workbenchModeService.ts
@@ -0,0 +1,147 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 nls from 'vs/nls';
+import { onUnexpectedError } from 'vs/base/common/errors';
+import * as paths from 'vs/base/common/paths';
+import { TPromise } from 'vs/base/common/winjs.base';
+import mime = require('vs/base/common/mime');
+import { IFilesConfiguration } from 'vs/platform/files/common/files';
+import { IExtensionService } from 'vs/platform/extensions/common/extensions';
+import { IExtensionPointUser, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry';
+import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
+import { ILanguageExtensionPoint, IValidLanguageExtensionPoint } from 'vs/editor/common/services/modeService';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { languagesExtPoint, ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
+
+export class WorkbenchModeServiceImpl extends ModeServiceImpl {
+ private _configurationService: IConfigurationService;
+ private _extensionService: IExtensionService;
+ private _onReadyPromise: TPromise;
+
+ constructor(
+ @IExtensionService extensionService: IExtensionService,
+ @IConfigurationService configurationService: IConfigurationService
+ ) {
+ super();
+ this._configurationService = configurationService;
+ this._extensionService = extensionService;
+
+ languagesExtPoint.setHandler((extensions: IExtensionPointUser[]) => {
+ let allValidLanguages: IValidLanguageExtensionPoint[] = [];
+
+ for (let i = 0, len = extensions.length; i < len; i++) {
+ let extension = extensions[i];
+
+ if (!Array.isArray(extension.value)) {
+ extension.collector.error(nls.localize('invalid', "Invalid `contributes.{0}`. Expected an array.", languagesExtPoint.name));
+ continue;
+ }
+
+ for (let j = 0, lenJ = extension.value.length; j < lenJ; j++) {
+ let ext = extension.value[j];
+ if (isValidLanguageExtensionPoint(ext, extension.collector)) {
+ let configuration = (ext.configuration ? paths.join(extension.description.extensionFolderPath, ext.configuration) : ext.configuration);
+ allValidLanguages.push({
+ id: ext.id,
+ extensions: ext.extensions,
+ filenames: ext.filenames,
+ filenamePatterns: ext.filenamePatterns,
+ firstLine: ext.firstLine,
+ aliases: ext.aliases,
+ mimetypes: ext.mimetypes,
+ configuration: configuration
+ });
+ }
+ }
+ }
+
+ ModesRegistry.registerLanguages(allValidLanguages);
+
+ });
+
+ this._configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(e.config));
+
+ this.onDidCreateMode((mode) => {
+ this._extensionService.activateByEvent(`onLanguage:${mode.getId()}`).done(null, onUnexpectedError);
+ });
+ }
+
+ protected _onReady(): TPromise {
+ if (!this._onReadyPromise) {
+ const configuration = this._configurationService.getConfiguration();
+ this._onReadyPromise = this._extensionService.onReady().then(() => {
+ this.onConfigurationChange(configuration);
+
+ return true;
+ });
+ }
+
+ return this._onReadyPromise;
+ }
+
+ private onConfigurationChange(configuration: IFilesConfiguration): void {
+
+ // Clear user configured mime associations
+ mime.clearTextMimes(true /* user configured */);
+
+ // Register based on settings
+ if (configuration.files && configuration.files.associations) {
+ Object.keys(configuration.files.associations).forEach(pattern => {
+ const langId = configuration.files.associations[pattern];
+ const mimetype = this.getMimeForMode(langId) || `text/x-${langId}`;
+
+ mime.registerTextMime({ id: langId, mime: mimetype, filepattern: pattern, userConfigured: true });
+ });
+ }
+ }
+}
+
+function isUndefinedOrStringArray(value: string[]): boolean {
+ if (typeof value === 'undefined') {
+ return true;
+ }
+ if (!Array.isArray(value)) {
+ return false;
+ }
+ return value.every(item => typeof item === 'string');
+}
+
+function isValidLanguageExtensionPoint(value: ILanguageExtensionPoint, collector: ExtensionMessageCollector): boolean {
+ if (!value) {
+ collector.error(nls.localize('invalid.empty', "Empty value for `contributes.{0}`", languagesExtPoint.name));
+ return false;
+ }
+ if (typeof value.id !== 'string') {
+ collector.error(nls.localize('require.id', "property `{0}` is mandatory and must be of type `string`", 'id'));
+ return false;
+ }
+ if (!isUndefinedOrStringArray(value.extensions)) {
+ collector.error(nls.localize('opt.extensions', "property `{0}` can be omitted and must be of type `string[]`", 'extensions'));
+ return false;
+ }
+ if (!isUndefinedOrStringArray(value.filenames)) {
+ collector.error(nls.localize('opt.filenames', "property `{0}` can be omitted and must be of type `string[]`", 'filenames'));
+ return false;
+ }
+ if (typeof value.firstLine !== 'undefined' && typeof value.firstLine !== 'string') {
+ collector.error(nls.localize('opt.firstLine', "property `{0}` can be omitted and must be of type `string`", 'firstLine'));
+ return false;
+ }
+ if (typeof value.configuration !== 'undefined' && typeof value.configuration !== 'string') {
+ collector.error(nls.localize('opt.configuration', "property `{0}` can be omitted and must be of type `string`", 'configuration'));
+ return false;
+ }
+ if (!isUndefinedOrStringArray(value.aliases)) {
+ collector.error(nls.localize('opt.aliases', "property `{0}` can be omitted and must be of type `string[]`", 'aliases'));
+ return false;
+ }
+ if (!isUndefinedOrStringArray(value.mimetypes)) {
+ collector.error(nls.localize('opt.mimetypes', "property `{0}` can be omitted and must be of type `string[]`", 'mimetypes'));
+ return false;
+ }
+ return true;
+}
diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts
index 9a0f4d24a8b..4410f3891a3 100644
--- a/src/vs/workbench/services/search/node/rawSearchService.ts
+++ b/src/vs/workbench/services/search/node/rawSearchService.ts
@@ -55,7 +55,7 @@ export class SearchService implements IRawSearchService {
}),
this.textSearchWorkerProvider);
- return this.doSearchWithBatchTimeout(engine, SearchService.BATCH_SIZE);
+ return this.doTextSearch(engine, SearchService.BATCH_SIZE);
}
public doFileSearch(EngineClass: { new (config: IRawSearch): ISearchEngine; }, config: IRawSearch, batchSize?: number): PPromise {
@@ -278,12 +278,13 @@ export class SearchService implements IRawSearchService {
});
}
- private doSearchWithBatchTimeout(engine: ISearchEngine, batchSize: number): PPromise> {
+ private doTextSearch(engine: TextSearchEngine, batchSize: number): PPromise> {
return new PPromise>((c, e, p) => {
// Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned
- const collector = new BatchedCollector(batchSize, p);
- engine.search((match) => {
- collector.addItem(match, match.numMatches);
+ const collector = new BatchedCollector(batchSize, p);
+ engine.search((matches) => {
+ const totalMatches = matches.reduce((acc, m) => acc + m.numMatches, 0);
+ collector.addItems(matches, totalMatches);
}, (progress) => {
p(progress);
}, (error, stats) => {
@@ -378,57 +379,51 @@ interface CacheStats {
}
/**
- * Collects a batch of items that each have a size. When the cumulative size of the batch reaches 'maxBatchSize', it calls the callback.
+ * Collects items that have a size - before the cumulative size of collected items reaches START_BATCH_AFTER_COUNT, the callback is called for every
+ * set of items collected.
+ * But after that point, the callback is called with batches of maxBatchSize.
* If the batch isn't filled within some time, the callback is also called.
- * And after 'runTimeoutUntilCount' items, the timeout is ignored, and the callback is called only when the batch is full.
*/
class BatchedCollector {
- // Use INIT_TIMEOUT for INIT_TIMEOUT_DURATION ms, then switch to LONGER_TIMEOUT
- private static INIT_TIMEOUT = 500;
- private static INIT_TIMEOUT_DURATION = 5000;
- private static LONGER_TIMEOUT = 2000;
+ private static TIMEOUT = 4000;
// After RUN_TIMEOUT_UNTIL_COUNT items have been collected, stop flushing on timeout
- private static RUN_TIMEOUT_UNTIL_COUNT = 50;
+ private static START_BATCH_AFTER_COUNT = 50;
private totalNumberCompleted = 0;
private batch: T[] = [];
private batchSize = 0;
private timeoutHandle: number;
- private startTime: number;
-
constructor(private maxBatchSize: number, private cb: (items: T | T[]) => void) {
}
- addItem(item: T, size: number): void {
- if (!item) {
+ addItems(items: T[], size: number): void {
+ if (!items) {
return;
}
if (this.maxBatchSize > 0) {
- this.addItemToBatch(item, size);
+ this.addItemsToBatch(items, size);
} else {
- this.cb(item);
+ this.cb(items);
}
}
- private addItemToBatch(item: T, size: number): void {
- if (!this.startTime) {
- this.startTime = Date.now();
- }
-
- this.batch.push(item);
+ private addItemsToBatch(items: T[], size: number): void {
+ this.batch = this.batch.concat(items);
this.batchSize += size;
- if (this.batchSize >= this.maxBatchSize) {
+ if (this.totalNumberCompleted < BatchedCollector.START_BATCH_AFTER_COUNT) {
+ // Flush because we aren't batching yet
+ this.flush();
+ } else if (this.batchSize >= this.maxBatchSize) {
// Flush because the batch is full
this.flush();
- } else if (!this.timeoutHandle && this.totalNumberCompleted < BatchedCollector.RUN_TIMEOUT_UNTIL_COUNT) {
+ } else if (!this.timeoutHandle) {
// No timeout running, start a timeout to flush
- const t = this.getTimeout();
this.timeoutHandle = setTimeout(() => {
this.flush();
- }, t);
+ }, BatchedCollector.TIMEOUT);
}
}
@@ -445,10 +440,4 @@ class BatchedCollector {
}
}
}
-
- private getTimeout(): number {
- return Date.now() - this.startTime < BatchedCollector.INIT_TIMEOUT_DURATION ?
- BatchedCollector.INIT_TIMEOUT :
- BatchedCollector.LONGER_TIMEOUT;
- }
}
diff --git a/src/vs/workbench/services/search/node/search.ts b/src/vs/workbench/services/search/node/search.ts
index 0bfd0556b4c..1ea0d55aa0b 100644
--- a/src/vs/workbench/services/search/node/search.ts
+++ b/src/vs/workbench/services/search/node/search.ts
@@ -37,7 +37,7 @@ export interface IRawFileMatch {
}
export interface ISearchEngine {
- search: (onResult: (match: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void) => void;
+ search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void) => void;
cancel: () => void;
}
diff --git a/src/vs/workbench/services/search/node/textSearch.ts b/src/vs/workbench/services/search/node/textSearch.ts
index eb82da7bf10..cb30c222466 100644
--- a/src/vs/workbench/services/search/node/textSearch.ts
+++ b/src/vs/workbench/services/search/node/textSearch.ts
@@ -15,7 +15,7 @@ import { ISerializedFileMatch, ISerializedSearchComplete, IRawSearch, ISearchEng
import { ISearchWorker } from './worker/searchWorkerIpc';
import { ITextSearchWorkerProvider } from './textSearchWorkerProvider';
-export class Engine implements ISearchEngine {
+export class Engine implements ISearchEngine {
private static PROGRESS_FLUSH_CHUNK_SIZE = 50; // optimization: number of files to process before emitting progress event
@@ -60,7 +60,7 @@ export class Engine implements ISearchEngine {
});
}
- search(onResult: (match: ISerializedFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void): void {
+ search(onResult: (match: ISerializedFileMatch[]) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchComplete) => void): void {
this.workers = this.workerProvider.getWorkers();
this.initializeWorkers();
@@ -100,10 +100,8 @@ export class Engine implements ISearchEngine {
}
const matches = result.matches;
+ onResult(matches);
this.numResults += result.numMatches;
- matches.forEach(m => {
- onResult(m);
- });
if (this.config.maxResults && this.numResults >= this.config.maxResults) {
// It's possible to go over maxResults like this, but it's much simpler than trying to extract the exact number
diff --git a/src/vs/workbench/services/search/node/worker/searchWorker.ts b/src/vs/workbench/services/search/node/worker/searchWorker.ts
index 4f845374d15..03b9e38957c 100644
--- a/src/vs/workbench/services/search/node/worker/searchWorker.ts
+++ b/src/vs/workbench/services/search/node/worker/searchWorker.ts
@@ -167,7 +167,7 @@ export class SearchWorkerEngine {
return new TPromise((resolve, reject) => {
fs.open(filename, 'r', null, (error: Error, fd: number) => {
if (error) {
- return reject(error);
+ return resolve(null);
}
let buffer = new Buffer(options.bufferLength);
@@ -275,7 +275,7 @@ export class SearchWorkerEngine {
readFile(/*isFirstRead=*/true, (error: Error) => {
if (error) {
- return reject(error);
+ return resolve(null);
}
if (line.length) {
@@ -283,11 +283,7 @@ export class SearchWorkerEngine {
}
fs.close(fd, (error: Error) => {
- if (error) {
- reject(error);
- } else {
- resolve(null);
- }
+ resolve(null);
});
});
});
diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts
index bf7e87287e8..a03f939b350 100644
--- a/src/vs/workbench/services/search/test/node/search.test.ts
+++ b/src/vs/workbench/services/search/test/node/search.test.ts
@@ -14,10 +14,14 @@ import * as platform from 'vs/base/common/platform';
import { LineMatch } from 'vs/platform/search/common/search';
import { FileWalker, Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch';
-import { IRawFileMatch } from 'vs/workbench/services/search/node/search';
+import { IRawFileMatch, ISerializedFileMatch } from 'vs/workbench/services/search/node/search';
import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch';
import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider';
+function countAll(matches: ISerializedFileMatch[]): number {
+ return matches.reduce((acc, m) => acc + count(m.lineMatches), 0);
+}
+
function count(lineMatches: LineMatch[]): number {
let count = 0;
if (lineMatches) {
@@ -628,8 +632,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, () => { }, (error) => {
assert.ok(!error);
@@ -649,8 +653,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, () => { }, (error) => {
assert.ok(!error);
@@ -670,8 +674,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, () => { }, (error) => {
assert.ok(!error);
@@ -691,8 +695,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, () => { }, (error) => {
assert.ok(!error);
@@ -712,8 +716,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, (result) => { }, (error) => {
assert.ok(!error);
@@ -734,8 +738,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, (result) => { }, (error) => {
assert.ok(!error);
@@ -756,8 +760,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, (result) => { }, (error) => {
assert.ok(!error);
@@ -779,8 +783,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, (result) => { }, (error) => {
assert.ok(!error);
@@ -801,8 +805,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, (result) => { }, (error) => {
assert.ok(!error);
@@ -825,8 +829,8 @@ suite('Search', () => {
let engine = new TextSearchEngine(config, new FileWalker(config), textSearchWorkerProvider);
engine.search((result) => {
- if (result && result.lineMatches) {
- c += count(result.lineMatches);
+ if (result) {
+ c += countAll(result);
}
}, (result) => { }, (error) => {
assert.ok(!error);
diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts
index 1353b624115..07ec93b6a1e 100644
--- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts
+++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts
@@ -28,7 +28,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
-import { ITelemetryService, anonymize } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { anonymize } from 'vs/platform/telemetry/common/telemetryUtils';
import { RunOnceScheduler } from 'vs/base/common/async';
/**
diff --git a/src/vs/workbench/test/browser/editorStacksModel.test.ts b/src/vs/workbench/test/browser/editorStacksModel.test.ts
index bf0099788ef..04dc3c4993b 100644
--- a/src/vs/workbench/test/browser/editorStacksModel.test.ts
+++ b/src/vs/workbench/test/browser/editorStacksModel.test.ts
@@ -19,7 +19,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { Registry } from 'vs/platform/platform';
import { Position, Direction } from 'vs/platform/editor/common/editor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import 'vs/workbench/browser/parts/editor/baseEditor';
diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts
index 8607a049eb2..b60e9ff06db 100644
--- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts
+++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts
@@ -13,7 +13,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import * as Platform from 'vs/platform/platform';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
diff --git a/src/vs/workbench/parts/indentation/test/common/indentationCommands.test.ts b/src/vs/workbench/test/common/editor/indentation.test.ts
similarity index 98%
rename from src/vs/workbench/parts/indentation/test/common/indentationCommands.test.ts
rename to src/vs/workbench/test/common/editor/indentation.test.ts
index d3d3c6951f6..397af6513ad 100644
--- a/src/vs/workbench/parts/indentation/test/common/indentationCommands.test.ts
+++ b/src/vs/workbench/test/common/editor/indentation.test.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Selection } from 'vs/editor/common/core/selection';
-import { IndentationToSpacesCommand, IndentationToTabsCommand } from 'vs/workbench/parts/indentation/common/indentationCommands';
+import { IndentationToSpacesCommand, IndentationToTabsCommand } from 'vs/workbench/common/editor/indentation';
import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils';
function testIndentationToSpacesCommand(lines: string[], selection: Selection, tabSize: number, expectedLines: string[], expectedSelection: Selection): void {
diff --git a/src/vs/workbench/test/common/editor/rangeDecorations.test.ts b/src/vs/workbench/test/common/editor/rangeDecorations.test.ts
index bde6f79405e..eee0aa2c901 100644
--- a/src/vs/workbench/test/common/editor/rangeDecorations.test.ts
+++ b/src/vs/workbench/test/common/editor/rangeDecorations.test.ts
@@ -151,7 +151,7 @@ suite('Editor - Range decorations', () => {
}
function mockEditorService(editorInput: IEditorInput);
- function mockEditorService(resource: URI)
+ function mockEditorService(resource: URI);
function mockEditorService(arg: any) {
let editorInput: IEditorInput = arg instanceof URI ? instantiationService.createInstance(FileEditorInput, arg, void 0) : arg;
instantiationService.stub(WorkbenchEditorService.IWorkbenchEditorService, 'getActiveEditorInput', editorInput);
diff --git a/src/vs/workbench/test/electron-browser/quickopen.perf.test.ts b/src/vs/workbench/test/electron-browser/quickopen.perf.test.ts
index 1d325306e0a..9757c297a15 100644
--- a/src/vs/workbench/test/electron-browser/quickopen.perf.test.ts
+++ b/src/vs/workbench/test/electron-browser/quickopen.perf.test.ts
@@ -11,7 +11,8 @@ import { WorkspaceContextService, IWorkspaceContextService } from 'vs/platform/w
import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ISearchService } from 'vs/platform/search/common/search';
-import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments, defaultExperiments } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry';
+import { defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils';
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import * as minimist from 'minimist';
@@ -71,7 +72,7 @@ suite('QuickOpen performance', () => {
const instantiationService = new InstantiationService(new ServiceCollection(
[ITelemetryService, telemetryService],
[IConfigurationService, new SimpleConfigurationService()],
- [IModelService, new ModelServiceImpl(null, configurationService, null)],
+ [IModelService, new ModelServiceImpl(null, configurationService)],
[IWorkspaceContextService, new WorkspaceContextService({ resource: URI.file(testWorkspacePath) })],
[IWorkbenchEditorService, new TestEditorService()],
[IEditorGroupService, new TestEditorGroupService()],
diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.test.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.test.ts
index ef12739a2c9..07be8c951d0 100644
--- a/src/vs/workbench/test/electron-browser/textsearch.perf.test.ts
+++ b/src/vs/workbench/test/electron-browser/textsearch.perf.test.ts
@@ -12,7 +12,8 @@ import { WorkspaceContextService, IWorkspaceContextService } from 'vs/platform/w
import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ISearchService, IQueryOptions } from 'vs/platform/search/common/search';
-import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments, defaultExperiments } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry';
+import { defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils';
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import * as minimist from 'minimist';
@@ -60,7 +61,7 @@ suite('TextSearch performance', () => {
const instantiationService = new InstantiationService(new ServiceCollection(
[ITelemetryService, telemetryService],
[IConfigurationService, new SimpleConfigurationService()],
- [IModelService, new ModelServiceImpl(null, configurationService, null)],
+ [IModelService, new ModelServiceImpl(null, configurationService)],
[IWorkspaceContextService, new WorkspaceContextService({ resource: URI.file(testWorkspacePath) })],
[IWorkbenchEditorService, new TestEditorService()],
[IEditorGroupService, new TestEditorGroupService()],
diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts
index ed4025be1e4..3fca6f90c18 100644
--- a/src/vs/workbench/test/workbenchTestServices.ts
+++ b/src/vs/workbench/test/workbenchTestServices.ts
@@ -12,7 +12,8 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
import { EventEmitter } from 'vs/base/common/eventEmitter';
import * as paths from 'vs/base/common/paths';
import URI from 'vs/base/common/uri';
-import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService';
import { IEditorGroup, ConfirmResult } from 'vs/workbench/common/editor';
import Event, { Emitter } from 'vs/base/common/event';