Merge remote-tracking branch 'origin/master' into tyriar/node-pty

This commit is contained in:
Daniel Imms
2017-01-06 23:59:22 -08:00
238 changed files with 4785 additions and 3384 deletions
+2
View File
@@ -9,6 +9,8 @@
a code editor with what developers need for their core edit-build-debug cycle. Code
provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools.
VS Code is updated monthly with new features and bug fixes. You can download it for Windows, Mac and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily.
<p align="center">
<img alt="VS Code in action" src="https://cloud.githubusercontent.com/assets/11839736/16642200/6624dde0-43bd-11e6-8595-c81885ba0dc2.png">
</p>
+3 -3
View File
@@ -39,8 +39,8 @@ const nodeModules = ['electron', 'original-fs']
// Build
const builtInExtensions = [
{ name: 'ms-vscode.node-debug', version: '1.9.0' },
{ name: 'ms-vscode.node-debug2', version: '1.9.0' }
{ name: 'ms-vscode.node-debug', version: '1.9.1' },
{ name: 'ms-vscode.node-debug2', version: '1.9.1' }
];
const vscodeEntryPoints = _.flatten([
@@ -110,7 +110,7 @@ const config = {
version: packageJson.electronVersion,
productAppName: product.nameLong,
companyName: 'Microsoft Corporation',
copyright: 'Copyright (C) 2016 Microsoft. All rights reserved',
copyright: 'Copyright (C) 2017 Microsoft. All rights reserved',
darwinIcon: 'resources/darwin/code.icns',
darwinBundleIdentifier: product.darwinBundleIdentifier,
darwinApplicationCategoryType: 'public.app-category.developer-tools',
+15 -15
View File
@@ -188,21 +188,21 @@ function format(text) {
}
function getDefaultOptions() {
return {
IndentSize: 4,
TabSize: 4,
NewLineCharacter: '\r\n',
ConvertTabsToSpaces: true,
IndentStyle: ts.IndentStyle.Block,
InsertSpaceAfterCommaDelimiter: true,
InsertSpaceAfterSemicolonInForStatements: true,
InsertSpaceBeforeAndAfterBinaryOperators: true,
InsertSpaceAfterKeywordsInControlFlowStatements: true,
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true,
PlaceOpenBraceOnNewLineForFunctions: false,
PlaceOpenBraceOnNewLineForControlBlocks: false,
indentSize: 4,
tabSize: 4,
newLineCharacter: '\r\n',
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Block,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
}
}
+15 -15
View File
@@ -217,22 +217,22 @@ function format(text:string): string {
function getDefaultOptions(): ts.FormatCodeOptions {
return {
IndentSize: 4,
TabSize: 4,
NewLineCharacter: '\r\n',
ConvertTabsToSpaces: true,
IndentStyle: ts.IndentStyle.Block,
indentSize: 4,
tabSize: 4,
newLineCharacter: '\r\n',
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Block,
InsertSpaceAfterCommaDelimiter: true,
InsertSpaceAfterSemicolonInForStatements: true,
InsertSpaceBeforeAndAfterBinaryOperators: true,
InsertSpaceAfterKeywordsInControlFlowStatements: true,
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true,
PlaceOpenBraceOnNewLineForFunctions: false,
PlaceOpenBraceOnNewLineForControlBlocks: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
}
}
+10
View File
@@ -141,6 +141,16 @@
"default": true,
"description": "%html.suggest.html5.desc%"
},
"html.validate.scripts": {
"type": "boolean",
"default": true,
"description": "%html.validate.scripts%"
},
"html.validate.styles": {
"type": "boolean",
"default": true,
"description": "%html.validate.styles%"
},
"html.trace.server": {
"type": "string",
"enum": [
+3 -1
View File
@@ -10,5 +10,7 @@
"html.format.extraLiners.desc": "List of tags, comma separated, that should have an extra newline before them. 'null' defaults to \"head, body, /html\".",
"html.suggest.angular1.desc": "Configures if the built-in HTML language support suggests Angular V1 tags and properties.",
"html.suggest.ionic.desc": "Configures if the built-in HTML language support suggests Ionic tags, properties and values.",
"html.suggest.html5.desc":"Configures if the built-in HTML language support suggests HTML5 tags, properties and values."
"html.suggest.html5.desc":"Configures if the built-in HTML language support suggests HTML5 tags, properties and values.",
"html.validate.scripts": "Configures if the built-in HTML language support validates embedded scripts.",
"html.validate.styles": "Configures if the built-in HTML language support validates embedded styles."
}
+17 -13
View File
@@ -6,9 +6,11 @@
import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType } from 'vscode-languageserver';
import { DocumentContext } from 'vscode-html-languageservice';
import { TextDocument, Diagnostic, DocumentLink, Range, TextEdit, SymbolInformation } from 'vscode-languageserver-types';
import { TextDocument, Diagnostic, DocumentLink, Range, SymbolInformation } from 'vscode-languageserver-types';
import { getLanguageModes, LanguageModes } from './modes/languageModes';
import { format } from './modes/formatting';
import * as url from 'url';
import * as path from 'path';
import uri from 'vscode-uri';
@@ -69,9 +71,18 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
};
});
let validation = {
html: true,
css: true,
javascript: true
};
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration((change) => {
settings = change.settings;
let validationSettings = settings && settings.html && settings.html.validate || {};
validation.css = validationSettings.styles !== false;
validation.javascript = validationSettings.scripts !== false;
languageModes.getAllModes().forEach(m => {
if (m.configure) {
@@ -115,7 +126,7 @@ function triggerValidation(textDocument: TextDocument): void {
function validateTextDocument(textDocument: TextDocument): void {
let diagnostics: Diagnostic[] = [];
languageModes.getAllModesInDocument(textDocument).forEach(mode => {
if (mode.doValidation) {
if (mode.doValidation && validation[mode.getId()]) {
pushAll(diagnostics, mode.doValidation(textDocument));
}
});
@@ -201,18 +212,11 @@ connection.onSignatureHelp(signatureHelpParms => {
connection.onDocumentRangeFormatting(formatParams => {
let document = documents.get(formatParams.textDocument.uri);
let ranges = languageModes.getModesInRange(document, formatParams.range);
let result: TextEdit[] = [];
let unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || '';
let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/), html: true };
ranges.forEach(r => {
let mode = r.mode;
if (mode && mode.format && enabledModes[mode.getId()] && !r.attributeValue) {
let edits = mode.format(document, r, formatParams.options);
pushAll(result, edits);
}
});
return result;
let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) };
return format(languageModes, document, formatParams.range, formatParams.options, enabledModes);
});
connection.onDocumentLinks(documentLinkParam => {
@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* 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 { applyEdits } from '../utils/edits';
import { TextDocument, Range, TextEdit, FormattingOptions } from 'vscode-languageserver-types';
import { LanguageModes } from './languageModes';
export function format(languageModes: LanguageModes, document: TextDocument, formatRange: Range, formattingOptions: FormattingOptions, enabledModes: { [mode: string]: boolean }) {
// run the html formatter on the full range and pass the result content to the embedded formatters.
// from the final content create a single edit
// advantages of this approach are
// - correct indents in the html document
// - correct initial indent for embedded formatters
// - no worrying of overlapping edits
// perform a html format and apply changes to a new document
let htmlMode = languageModes.getMode('html');
let htmlEdits = htmlMode.format(document, formatRange, formattingOptions);
let htmlFormattedContent = applyEdits(document, htmlEdits);
let newDocument = TextDocument.create(document.uri + '.tmp', document.languageId, document.version, htmlFormattedContent);
try {
// run embedded formatters on html formatted content: - formatters see correct initial indent
let afterFormatRangeLength = document.getText().length - document.offsetAt(formatRange.end); // length of unchanged content after replace range
let newFormatRange = Range.create(formatRange.start, newDocument.positionAt(htmlFormattedContent.length - afterFormatRangeLength));
let embeddedRanges = languageModes.getModesInRange(newDocument, newFormatRange);
let embeddedEdits: TextEdit[] = [];
for (let r of embeddedRanges) {
let mode = r.mode;
if (mode && mode.format && enabledModes[mode.getId()] && !r.attributeValue) {
let edits = mode.format(newDocument, r, formattingOptions);
for (let edit of edits) {
embeddedEdits.push(edit);
}
}
};
if (embeddedEdits.length === 0) {
return htmlEdits;
}
// apply all embedded format edits and create a single edit for all changes
let resultContent = applyEdits(newDocument, embeddedEdits);
let resultReplaceText = resultContent.substring(document.offsetAt(formatRange.start), resultContent.length - afterFormatRangeLength);
return [TextEdit.replace(formatRange, resultReplaceText)];
} finally {
languageModes.onDocumentRemoved(newDocument);
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService): LanguageM
settings = options && options.html;
},
doComplete(document: TextDocument, position: Position) {
let options = settings && settings.html && settings.html.suggest;
let options = settings && settings.suggest;
return htmlLanguageService.doComplete(document, position, htmlDocuments.get(document), options);
},
doHover(document: TextDocument, position: Position) {
@@ -8,6 +8,8 @@ import * as assert from 'assert';
import { getLanguageModes } from '../modes/languageModes';
import { TextDocument, Range, TextEdit, FormattingOptions } from 'vscode-languageserver-types';
import { format } from '../modes/formatting';
suite('HTML Embedded Formatting', () => {
function assertFormat(value: string, expected: string, options?: any): void {
@@ -31,15 +33,8 @@ suite('HTML Embedded Formatting', () => {
let range = Range.create(document.positionAt(rangeStartOffset), document.positionAt(rangeEndOffset));
let formatOptions = FormattingOptions.create(2, true);
let ranges = languageModes.getModesInRange(document, range);
let result: TextEdit[] = [];
ranges.forEach(r => {
let mode = r.mode;
if (mode && mode.format) {
let edits = mode.format(document, r, formatOptions);
pushAll(result, edits);
}
});
let result = format(languageModes, document, range, formatOptions, { css: true, javascript: true });
let actual = applyEdits(document, result);
assert.equal(actual, expected);
}
@@ -52,38 +47,38 @@ suite('HTML Embedded Formatting', () => {
test('HTML & Scripts', function (): any {
assertFormat('<html><head><script></script></head></html>', '<html>\n\n<head>\n <script></script>\n</head>\n\n</html>');
assertFormat('<html><head><script>var x=1;</script></head></html>', '<html>\n\n<head>\n <script>var x = 1;</script>\n</head>\n\n</html>');
assertFormat('<html><head><script>\nvar x=2;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 2;\n</script>\n</head>\n\n</html>');
assertFormat('<html><head>\n <script>\nvar x=3;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 3;\n </script>\n</head>\n\n</html>');
assertFormat('<html><head>\n <script>\nvar x=4;\nconsole.log("Hi");\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 4;\n console.log("Hi");\n </script>\n</head>\n\n</html>');
assertFormat('<html><head><script>var x=1;</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 1;\n </script>\n</head>\n\n</html>');
assertFormat('<html><head><script>\nvar x=2;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 2;\n\n </script>\n</head>\n\n</html>');
assertFormat('<html><head>\n <script>\nvar x=3;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 3;\n\n </script>\n</head>\n\n</html>');
assertFormat('<html><head>\n <script>\nvar x=4;\nconsole.log("Hi");\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 4;\n console.log("Hi");\n\n </script>\n</head>\n\n</html>');
assertFormat('<html><head>\n |<script>\nvar x=5;\n</script>|</head></html>', '<html><head>\n <script>\n var x = 5;\n </script></head></html>');
assertFormat('<html><head>\n |<script>\nvar x=5;\n</script>|</head></html>', '<html><head>\n <script>\n var x = 5;\n\n </script></head></html>');
assertFormat('<html><head>\n <script>\n|var x=6;|\n</script></head></html>', '<html><head>\n <script>\n var x = 6;\n</script></head></html>');
});
test('Script end tag', function (): any {
assertFormat('<html>\n<head>\n <script>\nvar x = 0;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 0;\n </script>\n</head>\n\n</html>');
assertFormat('<html>\n<head>\n <script>\nvar x = 0;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 0;\n\n </script>\n</head>\n\n</html>');
});
test('HTML & Multiple Scripts', function (): any {
assertFormat('<html><head>\n<script>\nif(x){\nbar(); }\n</script><script>\nfunction(x){}\n</script></head></html>', '<html>\n\n<head>\n <script>\n if (x) {\n bar();\n }\n</script>\n<script>\n function(x) { }\n</script>\n</head>\n\n</html>');
assertFormat('<html><head>\n<script>\nif(x){\nbar(); }\n</script><script>\nfunction(x){}\n</script></head></html>', '<html>\n\n<head>\n <script>\n if (x) {\n bar();\n }\n\n </script>\n <script>\n function(x) { }\n\n </script>\n</head>\n\n</html>');
});
test('HTML & Styles', function (): any {
assertFormat('<html><head>\n<style>\n.foo{display:none;}\n</style></head></html>', '<html>\n\n<head>\n <style>\n.foo{display:none;}\n</style>\n</head>\n\n</html>');
assertFormat('<html><head>\n<style>\n.foo{display:none;}\n</style></head></html>', '<html>\n\n<head>\n <style>\n .foo {\n display: none;\n }\n </style>\n</head>\n\n</html>');
});
test('EndWithNewline', function (): any {
let options = {
html: {
format: {
endWithNewline : true
endWithNewline: true
}
}
};
assertFormat('<html><body><p>Hello</p></body></html>', '<html>\n\n<body>\n <p>Hello</p>\n</body>\n\n</html>\n', options);
assertFormat('<html>|<body><p>Hello</p></body>|</html>', '<html><body>\n <p>Hello</p>\n</body></html>', options);
assertFormat('<html><head><script>\nvar x=1;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 1;\n</script>\n</head>\n\n</html>\n', options);
assertFormat('<html><head><script>\nvar x=1;\n</script></head></html>', '<html>\n\n<head>\n <script>\n var x = 1;\n\n </script>\n</head>\n\n</html>\n', options);
});
});
+34
View File
@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* 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 { TextDocument, TextEdit, Position } from 'vscode-languageserver-types';
export function applyEdits(document: TextDocument, edits: TextEdit[]): string {
let text = document.getText();
let sortedEdits = edits.sort((a, b) => {
let startDiff = comparePositions(a.range.start, b.range.start);
if (startDiff === 0) {
return comparePositions(a.range.end, b.range.end);
}
return startDiff;
});
let lastOffset = text.length;
sortedEdits.forEach(e => {
let startOffset = document.offsetAt(e.range.start);
let endOffset = document.offsetAt(e.range.end);
text = text.substring(0, startOffset) + e.newText + text.substring(endOffset, text.length);
lastOffset = startOffset;
});
return text;
}
function comparePositions(p1: Position, p2: Position) {
let diff = p2.line - p1.line;
if (diff === 0) {
return p2.character - p1.character;
}
return diff;
}
File diff suppressed because it is too large Load Diff
@@ -1584,8 +1584,8 @@
}
},
{
"c": "````application/json",
"t": "markdown.meta.paragraph",
"c": "````",
"t": "block.definition.fenced_code.markdown.markup.punctuation",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1594,9 +1594,20 @@
"hc_black": ".hc-black .token rgb(255, 255, 255)"
}
},
{
"c": "application/json",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"dark_vs": ".vs-dark .token rgb(212, 212, 212)",
"hc_black": ".hc-black .token rgb(255, 255, 255)",
"light_plus": ".vs .token rgb(0, 0, 0)",
"light_vs": ".vs .token rgb(0, 0, 0)"
},
"t": "block.fenced_code.language.markdown.markup"
},
{
"c": " { value: [\"or with a mime type\"] }",
"t": "markdown.meta.paragraph",
"t": "block.fenced_code.markdown.markup",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1607,7 +1618,7 @@
},
{
"c": "````",
"t": "markdown.meta.paragraph",
"t": "block.definition.fenced_code.markdown.markup.punctuation",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1827,7 +1838,7 @@
},
{
"c": "~~~",
"t": "markdown.meta.paragraph",
"t": "block.definition.fenced_code.markdown.markup.punctuation",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1838,7 +1849,7 @@
},
{
"c": "// Markdown extra adds un-indented code blocks too",
"t": "markdown.meta.paragraph",
"t": "block.fenced_code.markdown.markup",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1848,74 +1859,8 @@
}
},
{
"c": "if (this",
"t": "markdown.meta.paragraph",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
"dark_vs": ".vs-dark .token rgb(212, 212, 212)",
"light_vs": ".vs .token rgb(0, 0, 0)",
"hc_black": ".hc-black .token rgb(255, 255, 255)"
}
},
{
"c": "_",
"t": "definition.italic.markdown.markup.meta.paragraph.punctuation",
"r": {
"dark_plus": ".vs-dark.vscode-theme-defaults-themes-dark_plus-json .token.markup.italic rgb(212, 212, 212)",
"light_plus": ".vs.vscode-theme-defaults-themes-light_plus-json .token.markup.italic rgb(0, 0, 0)",
"dark_vs": ".vs-dark.vscode-theme-defaults-themes-dark_vs-json .token.markup.italic rgb(212, 212, 212)",
"light_vs": ".vs.vscode-theme-defaults-themes-light_vs-json .token.markup.italic rgb(0, 0, 0)",
"hc_black": ".hc-black.vscode-theme-defaults-themes-hc_black-json .token.markup.italic rgb(255, 255, 255)"
}
},
{
"c": "is",
"t": "italic.markdown.markup.meta.paragraph",
"r": {
"dark_plus": ".vs-dark.vscode-theme-defaults-themes-dark_plus-json .token.markup.italic rgb(212, 212, 212)",
"light_plus": ".vs.vscode-theme-defaults-themes-light_plus-json .token.markup.italic rgb(0, 0, 0)",
"dark_vs": ".vs-dark.vscode-theme-defaults-themes-dark_vs-json .token.markup.italic rgb(212, 212, 212)",
"light_vs": ".vs.vscode-theme-defaults-themes-light_vs-json .token.markup.italic rgb(0, 0, 0)",
"hc_black": ".hc-black.vscode-theme-defaults-themes-hc_black-json .token.markup.italic rgb(255, 255, 255)"
}
},
{
"c": "_",
"t": "definition.italic.markdown.markup.meta.paragraph.punctuation",
"r": {
"dark_plus": ".vs-dark.vscode-theme-defaults-themes-dark_plus-json .token.markup.italic rgb(212, 212, 212)",
"light_plus": ".vs.vscode-theme-defaults-themes-light_plus-json .token.markup.italic rgb(0, 0, 0)",
"dark_vs": ".vs-dark.vscode-theme-defaults-themes-dark_vs-json .token.markup.italic rgb(212, 212, 212)",
"light_vs": ".vs.vscode-theme-defaults-themes-light_vs-json .token.markup.italic rgb(0, 0, 0)",
"hc_black": ".hc-black.vscode-theme-defaults-themes-hc_black-json .token.markup.italic rgb(255, 255, 255)"
}
},
{
"c": "more_code == true ",
"t": "markdown.meta.paragraph",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
"dark_vs": ".vs-dark .token rgb(212, 212, 212)",
"light_vs": ".vs .token rgb(0, 0, 0)",
"hc_black": ".hc-black .token rgb(255, 255, 255)"
}
},
{
"c": "&&",
"t": "markdown.meta.other.paragraph.valid-ampersand",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
"dark_vs": ".vs-dark .token rgb(212, 212, 212)",
"light_vs": ".vs .token rgb(0, 0, 0)",
"hc_black": ".hc-black .token rgb(255, 255, 255)"
}
},
{
"c": " !indented) {",
"t": "markdown.meta.paragraph",
"c": "if (this_is_more_code == true && !indented) {",
"t": "block.fenced_code.markdown.markup",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1926,7 +1871,7 @@
},
{
"c": " // tild wrapped code blocks, also not indented",
"t": "markdown.meta.paragraph",
"t": "block.fenced_code.markdown.markup",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1937,7 +1882,7 @@
},
{
"c": "}",
"t": "markdown.meta.paragraph",
"t": "block.fenced_code.markdown.markup",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1948,7 +1893,7 @@
},
{
"c": "~~~",
"t": "markdown.meta.paragraph",
"t": "block.definition.fenced_code.markdown.markup.punctuation",
"r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)",
+5
View File
@@ -99,6 +99,11 @@
"default": true,
"description": "%typescript.check.tscVersion%"
},
"typescript.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%typescript.referencesCodeLens.enabled%"
},
"typescript.tsserver.trace": {
"type": "string",
"enum": [
+2 -1
View File
@@ -24,5 +24,6 @@
"format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Defines space handling after opening and before closing JSX expression braces. Requires TypeScript >= 2.0.6.",
"format.placeOpenBraceOnNewLineForFunctions": "Defines whether an open brace is put onto a new line for functions or not.",
"format.placeOpenBraceOnNewLineForControlBlocks": "Defines whether an open brace is put onto a new line for control blocks or not.",
"javascript.validate.enable": "Enable/disable JavaScript validation."
"javascript.validate.enable": "Enable/disable JavaScript validation.",
"typescript.referencesCodeLens.enabled": "Enable/disable the references code lens"
}
@@ -5,7 +5,7 @@
'use strict';
import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, Uri, workspace, WorkspaceEdit, TextEdit } from 'vscode';
import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, Uri, workspace, WorkspaceEdit, TextEdit, Position } from 'vscode';
import * as Proto from '../protocol';
import { ITypescriptServiceClient } from '../typescriptService';
@@ -46,7 +46,7 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider
public provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): Thenable<Command[]> {
const file = this.client.asAbsolutePath(document.uri);
if (!file) {
return Promise.resolve(null);
return Promise.resolve<Command[]>([]);
}
const source: Source = {
@@ -99,11 +99,29 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider
private onCodeAction(source: Source, workspaceEdit: WorkspaceEdit) {
workspace.applyEdit(workspaceEdit).then(success => {
if (!success) {
return Promise.reject(null);
return Promise.reject<boolean>(false);
}
let firstEdit: TextEdit | null = null;
for (const [uri, edits] of workspaceEdit.entries()) {
if (uri.fsPath === source.uri.fsPath) {
firstEdit = edits[0];
break;
}
}
if (!firstEdit) {
return true;
}
const newLines = firstEdit.newText.match(/\n/g);
const editedRange = new Range(
new Position(firstEdit.range.start.line, 0),
new Position(firstEdit.range.end.line + 1 + (newLines ? newLines.length : 0), 0));
// TODO: Workaround for https://github.com/Microsoft/TypeScript/issues/12249
// apply formatting to the source range until TS returns formatted results
return commands.executeCommand('vscode.executeFormatRangeProvider', source.uri, source.range, {}).then((edits: TextEdit[]) => {
return commands.executeCommand('vscode.executeFormatRangeProvider', source.uri, editedRange, {}).then((edits: TextEdit[]) => {
if (!edits || !edits.length) {
return false;
}
@@ -119,7 +119,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise<CompletionItem[]> {
if (this.typingsStatus.isAcquiringTypings) {
return Promise.reject({
return Promise.reject<CompletionItem[]>({
label: localize('acquiringTypingsLabel', 'Acquiring typings...'),
detail: localize('acquiringTypingsDetail', 'Acquiring typings definitions for IntelliSense.')
});
@@ -220,7 +220,6 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
// Don't complete function calls inside of destructive assigments or imports
return this.client.execute('quickinfo', args).then(infoResponse => {
const info = infoResponse.body;
console.log(info && info.kind);
switch (info && info.kind) {
case 'var':
case 'let':
@@ -0,0 +1,144 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { CodeLensProvider, CodeLens, CancellationToken, TextDocument, Range, Uri, Location, Position, workspace, WorkspaceConfiguration } from 'vscode';
import * as Proto from '../protocol';
import * as PConst from '../protocol.const';
import { ITypescriptServiceClient } from '../typescriptService';
import * as nls from 'vscode-nls';
let localize = nls.loadMessageBundle();
class ReferencesCodeLens extends CodeLens {
public document: Uri;
public file: string;
constructor(document: Uri, file: string, range: Range) {
super(range);
this.document = document;
this.file = file;
}
}
export default class TypeScriptReferencesCodeLensProvider implements CodeLensProvider {
private client: ITypescriptServiceClient;
private enabled = false;
constructor(client: ITypescriptServiceClient) {
this.client = client;
}
public updateConfiguration(config: WorkspaceConfiguration): void {
let typeScriptConfig = workspace.getConfiguration('typescript');
this.enabled = typeScriptConfig.get('referencesCodeLens.enabled', false);
}
provideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
if (!this.enabled) {
return Promise.resolve([]);
}
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve([]);
}
return this.client.execute('navtree', { file: filepath }, token).then(response => {
const tree = response.body;
const referenceableSpans: Range[] = [];
if (tree && tree.childItems) {
tree.childItems.forEach(item => this.extractReferenceableSymbols(document, item, referenceableSpans));
}
return Promise.resolve(referenceableSpans.map(span => new ReferencesCodeLens(document.uri, filepath, span)));
});
}
resolveCodeLens(inputCodeLens: CodeLens, token: CancellationToken): Promise<CodeLens> {
const codeLens = inputCodeLens as ReferencesCodeLens;
if (!codeLens.document) {
return Promise.reject<CodeLens>(codeLens);
}
const args: Proto.FileLocationRequestArgs = {
file: codeLens.file,
line: codeLens.range.start.line + 1,
offset: codeLens.range.start.character + 1
};
return this.client.execute('references', args, token).then(response => {
if (response && response.body) {
const referenceCount = Math.max(0, response.body.refs.length - 1);
const locations = response.body.refs.map(reference =>
new Location(Uri.file(reference.file),
new Range(
new Position(reference.start.line - 1, reference.start.offset - 1),
new Position(reference.end.line - 1, reference.end.offset - 1))));
codeLens.command = {
title: referenceCount + ' ' + (referenceCount === 1 ? localize('oneReferenceLabel', 'reference') : localize('manyReferenceLabel', 'references')),
command: 'editor.action.showReferences',
arguments: [codeLens.document, codeLens.range.start, locations]
};
return Promise.resolve(codeLens);
}
return Promise.reject(codeLens);
}).catch(() => {
codeLens.command = {
title: localize('referenceErrorLabel', 'Could not determine references'),
command: ''
};
return Promise.resolve(codeLens);
});
}
private extractReferenceableSymbols(document: TextDocument, item: Proto.NavigationTree, results: Range[]) {
if (!item) {
return;
}
const span = item.spans && item.spans[0];
if (span) {
const range = new Range(
new Position(span.start.line - 1, span.start.offset - 1),
new Position(span.end.line - 1, span.end.offset - 1));
// TODO: TS currently requires the position for 'references 'to be inside of the identifer
// Massage the range to make sure this is the case
const text = document.getText(range);
switch (item.kind) {
case PConst.Kind.const:
case PConst.Kind.let:
case PConst.Kind.variable:
case PConst.Kind.function:
// Only show references for exported variables
if (!item.kindModifiers.match(/\bexport\b/)) {
break;
}
// fallthrough
case PConst.Kind.memberFunction:
case PConst.Kind.memberVariable:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
case PConst.Kind.constructorImplementation:
case PConst.Kind.class:
case PConst.Kind.interface:
case PConst.Kind.type:
case PConst.Kind.enum:
const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${item.text}`, 'g');
const match = identifierMatch.exec(text);
const start = match ? match.index + match[1].length : 0;
results.push(new Range(
new Position(range.start.line, range.start.character + start),
new Position(range.start.line, range.start.character + start + item.text.length)));
break;
}
}
(item.childItems || []).forEach(item => this.extractReferenceableSymbols(document, item, results));
}
};
@@ -35,6 +35,7 @@ import BufferSyncSupport from './features/bufferSyncSupport';
import CompletionItemProvider from './features/completionItemProvider';
import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider';
import ReferenceCodeLensProvider from './features/referencesCodeLensProvider';
import * as BuildStatus from './utils/buildStatus';
import * as ProjectStatus from './utils/projectStatus';
@@ -107,6 +108,7 @@ class LanguageProvider {
private formattingProvider: FormattingProvider;
private formattingProviderRegistration: Disposable | null;
private typingsStatus: TypingsStatus;
private referenceCodeLensProvider: ReferenceCodeLensProvider;
private _validate: boolean;
@@ -156,6 +158,12 @@ class LanguageProvider {
this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
}
this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client);
this.referenceCodeLensProvider.updateConfiguration(config);
if (client.apiVersion.has206Features()) {
languages.registerCodeLensProvider(this.description.modeIds, this.referenceCodeLensProvider);
}
this.description.modeIds.forEach(modeId => {
let selector: DocumentFilter = { scheme: 'file', language: modeId };
languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.');
@@ -171,6 +179,7 @@ class LanguageProvider {
if (client.apiVersion.has213Features()) {
languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId));
}
languages.setLanguageConfiguration(modeId, {
indentationRules: {
// ^(.*\*/)?\s*\}.*$
@@ -217,6 +226,9 @@ class LanguageProvider {
if (this.completionItemProvider) {
this.completionItemProvider.updateConfiguration(config);
}
if (this.referenceCodeLensProvider) {
this.referenceCodeLensProvider.updateConfiguration(config);
}
if (this.formattingProvider) {
this.formattingProvider.updateConfiguration(config);
if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {
@@ -346,17 +346,20 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
switch (selected.id) {
case MessageAction.useLocal:
let pathValue = './node_modules/typescript/lib';
tsConfig.update('tsdk', pathValue, false);
window.showInformationMessage(localize('updatedtsdk', 'Updated workspace setting \'typescript.tsdk\' to {0}', pathValue));
tsConfig.update('tsdk', pathValue, false).then(
() => window.showInformationMessage(localize('updatedtsdk', 'Updated workspace setting \'typescript.tsdk\' to {0}', pathValue)),
() => window.showErrorMessage(localize('updateTsdkFailed', 'Could not update the \'typescript.tsdk\' workspace setting. Please check that the workspace settings file is valid')));
showVersionStatusItem = true;
return localModulePath;
case MessageAction.useBundled:
tsConfig.update(checkWorkspaceVersionKey, false, false);
window.showInformationMessage(localize('updateLocalWorkspaceCheck', 'Updated workspace setting \'typescript.check.workspaceVersion\' to false'));
tsConfig.update(checkWorkspaceVersionKey, false, false).then(
() => window.showInformationMessage(localize('updateLocalWorkspaceCheck', 'Updated workspace setting \'typescript.check.workspaceVersion\' to false')),
() => window.showErrorMessage(localize('updateLocalWorkspaceCheckFailed', 'Could not update the \'typescript.check.workspaceVersion\' workspace setting. Please check that the workspace settings file is valid')));
return modulePath;
case MessageAction.neverCheckLocalVersion:
window.showInformationMessage(localize('updateGlobalWorkspaceCheck', 'Updated user setting \'typescript.check.workspaceVersion\' to false'));
tsConfig.update(checkWorkspaceVersionKey, false, true);
tsConfig.update(checkWorkspaceVersionKey, false, true).then(
() => window.showInformationMessage(localize('updateGlobalWorkspaceCheck', 'Updated user setting \'typescript.check.workspaceVersion\' to false')),
() => window.showErrorMessage(localize('updateGlobalWorkspaceCheckFailed', 'Could not update \'typescript.check.workspaceVersion\' user setting. Please check that your user settings file is valid')));
return modulePath;
default:
return modulePath;
+5 -5
View File
@@ -2,7 +2,7 @@
"name": "code-oss-dev",
"version": "1.9.0",
"electronVersion": "1.4.6",
"distro": "2eecc8b68318fba1fc5b62930287914ac9225e8a",
"distro": "ef07477c3bbf2aa2f274b13093cbe0d96fa59fdd",
"author": {
"name": "Microsoft Corporation"
},
@@ -48,6 +48,7 @@
"eslint": "^3.4.0",
"event-stream": "^3.1.7",
"express": "^4.13.1",
"flatpak-bundler": "^0.1.1",
"ghooks": "1.0.3",
"glob": "^5.0.13",
"gulp": "^3.8.9",
@@ -59,6 +60,7 @@
"gulp-cssnano": "^2.1.0",
"gulp-filter": "^3.0.0",
"gulp-flatmap": "^1.0.0",
"gulp-image-resize": "^0.10.0",
"gulp-json-editor": "^2.2.1",
"gulp-mocha": "^2.1.3",
"gulp-remote-src": "^0.4.0",
@@ -71,8 +73,6 @@
"gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.6",
"gulp-vinyl-zip": "^1.2.2",
"gulp-image-resize": "^0.10.0",
"flatpak-bundler": "^0.1.1",
"innosetup-compiler": "^5.5.60",
"is": "^3.1.0",
"istanbul": "^0.3.17",
@@ -91,8 +91,8 @@
"sinon": "^1.17.2",
"source-map": "^0.4.4",
"tslint": "^3.3.0",
"typescript": "2.0.3",
"typescript-formatter": "3.1.0",
"typescript": "^2.1.4",
"typescript-formatter": "4.0.1",
"uglify-js": "2.4.8",
"underscore": "^1.8.2",
"vinyl": "^0.4.5",
+1 -2
View File
@@ -4,7 +4,6 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# If root, ensure that --user-data-dir is specified
ARGS=$@
if [ "$(id -u)" = "0" ]; then
while test $# -gt 0
do
@@ -34,5 +33,5 @@ fi
ELECTRON="$VSCODE_PATH/@@NAME@@"
CLI="$VSCODE_PATH/resources/app/out/cli.js"
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" $ARGS
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@"
exit $?
+1 -1
View File
@@ -2,7 +2,7 @@
Name=@@NAME_LONG@@
Comment=Code Editing. Redefined.
GenericName=Text Editor
Exec=/usr/share/@@NAME@@/@@NAME@@ %U
Exec=/usr/share/@@NAME@@/@@NAME@@ --new-window-if-not-first %U
Icon=@@NAME@@
Type=Application
StartupNotify=true
+1
View File
@@ -180,6 +180,7 @@ var nodeCachedDataDir = getNodeCachedDataDir().then(function (value) {
// Load our code once ready
app.once('ready', function () {
global.perfAppReady = Date.now();
var nlsConfig = getNLSConfiguration();
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig);
+1 -1
View File
@@ -692,7 +692,7 @@ export class Builder implements IDisposable {
}
};
return this.on(arg1, fn, listenerToUnbindContainer);
return this.on(arg1, fn, listenerToUnbindContainer, useCapture);
}
/**
+3 -3
View File
@@ -114,14 +114,14 @@ export interface IDomEvent {
(element: EventHandler, type: string, useCapture?: boolean): _Event<any>;
}
export const domEvent: IDomEvent = (element: EventHandler, type: string, useCapture?) => {
export const domEvent: IDomEvent = (element: EventHandler, type: string, useCapture?: boolean) => {
const fn = e => emitter.fire(e);
const emitter = new Emitter<any>({
onFirstListenerAdd: () => {
element.addEventListener(type, fn);
element.addEventListener(type, fn, useCapture);
},
onLastListenerRemove: () => {
element.removeEventListener(type, fn);
element.removeEventListener(type, fn, useCapture);
}
});
+1 -1
View File
@@ -91,7 +91,7 @@ function _renderHtml(content: IHTMLContentElement, options: RenderOptions = {}):
const renderer = new marked.Renderer();
renderer.image = (href: string, title: string, text: string) => {
let dimensions = [];
let dimensions: string[] = [];
if (href) {
const splitted = href.split('|').map(s => s.trim());
href = splitted[0];
+4 -3
View File
@@ -7,7 +7,7 @@ import 'vs/css!./list';
import { IDisposable } from 'vs/base/common/lifecycle';
import { range } from 'vs/base/common/arrays';
import { IDelegate, IRenderer, IFocusChangeEvent, ISelectionChangeEvent } from './list';
import { List } from './listWidget';
import { List, IListOptions } from './listWidget';
import { IPagedModel } from 'vs/base/common/paging';
import Event, { mapEvent } from 'vs/base/common/event';
@@ -67,10 +67,11 @@ export class PagedList<T> {
constructor(
container: HTMLElement,
delegate: IDelegate<number>,
renderers: IPagedRenderer<T, any>[]
renderers: IPagedRenderer<T, any>[],
options: IListOptions = {}
) {
const pagedRenderers = renderers.map(r => new PagedRenderer<T, ITemplateData<T>>(r, () => this.model));
this.list = new List(container, delegate, pagedRenderers);
this.list = new List(container, delegate, pagedRenderers, options);
}
get onFocusChange(): Event<IFocusChangeEvent<T>> {
+1 -1
View File
@@ -240,7 +240,7 @@ export class ListView<T> implements IDisposable {
return DOM.addDisposableListener(domNode, type, handler, useCapture);
}
private fireScopedEvent(handler: (event: any) => void, index) {
private fireScopedEvent(handler: (event: any) => void, index: number) {
if (index < 0) {
return;
}
+19 -5
View File
@@ -65,7 +65,7 @@ class Trait<T> implements IDisposable {
splice(start: number, deleteCount: number, insertCount: number): void {
const diff = insertCount - deleteCount;
const end = start + deleteCount;
const indexes = [];
const indexes: number[] = [];
for (let index of indexes) {
if (index >= start && index < end) {
@@ -110,13 +110,13 @@ class Trait<T> implements IDisposable {
class FocusTrait<T> extends Trait<T> {
constructor(private getElementId: (number) => string) {
constructor(private getElementId: (number: number) => string) {
super('focused');
}
renderElement(element: T, index: number, container: HTMLElement): void {
super.renderElement(element, index, container);
container.setAttribute('role', 'option');
container.setAttribute('role', 'treeitem');
container.setAttribute('id', this.getElementId(index));
}
}
@@ -201,6 +201,7 @@ class Controller<T> implements IDisposable {
}
export interface IListOptions extends IListViewOptions {
ariaLabel?: string;
}
const DefaultOptions: IListOptions = {};
@@ -245,13 +246,17 @@ export class List<T> implements IDisposable {
});
this.view = new ListView(container, delegate, renderers, options);
this.view.domNode.setAttribute('role', 'listbox');
this.view.domNode.setAttribute('role', 'tree');
this.view.domNode.tabIndex = 0;
this.controller = new Controller(this, this.view);
this.disposables = [this.focus, this.selection, this.view, this.controller];
this._onDOMFocus = domEvent(this.view.domNode, 'focus');
this.onFocusChange(this._onFocusChange, this, this.disposables);
if (options.ariaLabel) {
this.view.domNode.setAttribute('aria-label', options.ariaLabel);
}
}
splice(start: number, deleteCount: number, ...elements: T[]): void {
@@ -418,7 +423,16 @@ export class List<T> implements IDisposable {
}
private _onFocusChange(): void {
DOM.toggleClass(this.view.domNode, 'element-focused', this.focus.get().length > 0);
const focus = this.focus.get();
if (focus.length > 0) {
this.view.domNode.setAttribute('aria-activedescendant', this.getElementId(focus[0]));
} else {
this.view.domNode.removeAttribute('aria-activedescendant');
}
this.view.domNode.setAttribute('role', 'tree');
DOM.toggleClass(this.view.domNode, 'element-focused', focus.length > 0);
}
dispose(): void {
+4 -4
View File
@@ -25,22 +25,22 @@
cursor: default !important;
}
.vertical-cursor-container * {
.vertical-cursor-container {
cursor: ew-resize;
}
.horizontal-cursor-container * {
.horizontal-cursor-container {
cursor: ns-resize;
}
/** Custom Mac Cursor */
.monaco-sash.mac.vertical,
.vertical-cursor-container-mac * {
.vertical-cursor-container-mac {
cursor: col-resize;
}
.monaco-sash.mac.horizontal,
.horizontal-cursor-container-mac * {
.horizontal-cursor-container-mac {
cursor: row-resize;
}
-29
View File
@@ -1,29 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.benchmarktimerbox {
z-index: 100;
position: absolute;
color: black;
background: lightblue;
top: 100px;
right: 20px;
font-family: monospace;
}
.benchmarktimerbox .inner {
width: 600px;
height: 300px;
overflow: scroll;
}
.benchmarktimerbox .timeFilter {
width: 50px;
}
.benchmarktimerbox pre {
margin: 0;
}
.timer-event-1 { background: rgba(190, 191, 193, 0.4); color: black; }
-281
View File
@@ -1,281 +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 'vs/css!./timer';
import { TimeKeeper, ITimerEvent, getTimeKeeper } from 'vs/base/common/timer';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import DomUtils = require('vs/base/browser/dom');
export class TimeKeeperRenderer {
private listenersToRemove: IDisposable[];
private timeKeeper: TimeKeeper;
private outerDomNode: HTMLElement;
private domNode: HTMLElement;
private renderCnt: number;
private lastEventIndex: number;
private textFilter: string;
private textFilterDomNode: HTMLInputElement;
private timeFilter: number;
private timeFilterDomNode: HTMLInputElement;
private intervalTokenId: number;
private renderedEvents: {
[key: string]: ITimerEvent;
};
private onHide: () => void;
constructor(onHide: () => void) {
this.timeKeeper = getTimeKeeper();
this.onHide = onHide;
this.lastEventIndex = 0;
this.renderedEvents = {};
this.renderCnt = 0;
this.listenersToRemove = [];
this.domNode = this._createDomNode();
this.intervalTokenId = window.setInterval(() => this._render(), 500);
}
public destroy(): void {
document.body.removeChild(this.outerDomNode);
window.clearInterval(this.intervalTokenId);
this.listenersToRemove = dispose(this.listenersToRemove);
}
private _createDomNode(): HTMLElement {
this.outerDomNode = document.createElement('div');
this.outerDomNode.className = 'benchmarktimerbox';
// Clear
let cancel: HTMLInputElement = <HTMLInputElement>document.createElement('input');
cancel.type = 'button';
cancel.value = 'Clear';
this.listenersToRemove.push(DomUtils.addDisposableListener(cancel, 'click', () => this._onClear()));
this.outerDomNode.appendChild(cancel);
// Text filter
this.textFilterDomNode = <HTMLInputElement>document.createElement('input');
this.textFilterDomNode.type = 'text';
this.textFilterDomNode.className = 'textFilter';
this.listenersToRemove.push(DomUtils.addDisposableListener(this.textFilterDomNode, 'keydown', () => this.onTextFilterChange()));
this.textFilter = '';
this.outerDomNode.appendChild(document.createTextNode('Filter'));
this.outerDomNode.appendChild(this.textFilterDomNode);
// Time filter
this.timeFilterDomNode = <HTMLInputElement>document.createElement('input');
this.timeFilterDomNode.type = 'text';
this.timeFilterDomNode.value = '0';
this.timeFilterDomNode.className = 'timeFilter';
this.listenersToRemove.push(DomUtils.addDisposableListener(this.timeFilterDomNode, 'keydown', () => this.onTimeFilterChange()));
this.timeFilter = 0;
this.outerDomNode.appendChild(document.createTextNode('Hide time under'));
this.outerDomNode.appendChild(this.timeFilterDomNode);
let hide: HTMLInputElement = <HTMLInputElement>document.createElement('input');
hide.type = 'button';
hide.value = 'Close';
this.listenersToRemove.push(DomUtils.addDisposableListener(hide, 'click', () => {
this.onHide();
}));
this.outerDomNode.appendChild(hide);
let heading = document.createElement('pre');
heading.appendChild(document.createTextNode(this.renderRow('TOPIC', 'NAME', 'TOOK', 'START', 'END')));
this.outerDomNode.appendChild(heading);
this.outerDomNode.appendChild(document.createElement('hr'));
let domNode = document.createElement('div');
domNode.className = 'inner';
this.outerDomNode.appendChild(domNode);
document.body.appendChild(this.outerDomNode);
return domNode;
}
private onTextFilterChange(): void {
setTimeout(() => {
this.refilter();
});
}
private onTimeFilterChange(): void {
setTimeout(() => {
this.refilter();
});
}
private matchesTextFilter(event: ITimerEvent): boolean {
if (!this.textFilter) {
return true;
}
if (event.topic.toLowerCase().indexOf(this.textFilter.toLowerCase()) >= 0) {
return true;
}
if (event.name.toLowerCase().indexOf(this.textFilter.toLowerCase()) >= 0) {
return true;
}
return false;
}
private matchesTimeFilter(event: ITimerEvent): boolean {
if (!this.timeFilter) {
return true;
}
if (event.timeTaken() >= this.timeFilter) {
return true;
}
return false;
}
private shouldShow(event: ITimerEvent): boolean {
return this.matchesTextFilter(event) && this.matchesTimeFilter(event);
}
private refilter(): void {
this.textFilter = this.textFilterDomNode.value;
this.timeFilter = parseInt(this.timeFilterDomNode.value, 10);
let domNodes = Array.prototype.slice.call(this.domNode.children, 0);
for (let i = 0; i < domNodes.length; i++) {
let eventId = domNodes[i].getAttribute('data-event-id');
let event = this.renderedEvents[eventId];
if (this.shouldShow(event)) {
domNodes[i].style.display = 'inherit';
} else {
domNodes[i].style.display = 'none';
}
}
}
private _onClear(): void {
this.lastEventIndex = this.timeKeeper.getCollectedEvents().length;
this.renderedEvents = {};
this.renderCnt = 0;
DomUtils.clearNode(this.domNode);
}
private leftPaddedString(size: number, padChar: string, str: string): string {
let spaces = this._repeatStr(padChar, Math.max(0, size - str.length));
return spaces + str;
}
private rightPaddedString(size: number, padChar: string, str: string): string {
let spaces = this._repeatStr(padChar, Math.max(0, size - str.length));
return str + spaces;
}
private renderRow(topic: string, name: string, timeTook: string, timeStart: string, timerEnd: string): string {
let result = ' ';
result += this.rightPaddedString(10, ' ', topic);
result += this.rightPaddedString(30, ' ', name);
result += ' ' + this.leftPaddedString(15, ' ', timeTook);
result += ' ' + this.leftPaddedString(13, ' ', timeStart);
return result;
}
private _suffix0(s: string): string {
if (s.charAt(s.length - 3) === '.') {
return s;
}
if (s.charAt(s.length - 2) === '.') {
return s + '0';
}
return s + '.00';
}
private _twoPrecision(a: number): string {
return this._suffix0(Math.round(a * 100) / 100 + '');
}
private _absoluteTime(t: number): string {
if (t < 1000) {
return this._twoPrecision(t) + ' ms';
}
t /= 1000;
if (t < 60) {
return this._twoPrecision(t) + ' s';
}
t /= 60;
if (t < 60) {
return this._twoPrecision(t) + ' m';
}
t /= 60;
return this._twoPrecision(t) + ' h';
}
private _renderEvent(domNode: HTMLElement, event: ITimerEvent): void {
let start = event.startTime.getTime() - TimeKeeper.PARSE_TIME.getTime();
let result = this.renderRow(
event.topic,
event.name,
this._twoPrecision(event.timeTaken()),
this._absoluteTime(start) + '',
this._absoluteTime(start + event.timeTaken())
);
domNode.textContent = '';
domNode.appendChild(document.createTextNode(result));
}
private _renderStartTimerEvent(event: ITimerEvent): void {
let domNode = document.createElement('pre');
this._renderEvent(domNode, event);
this.domNode.appendChild(domNode);
let idString = event.id.toString();
domNode.setAttribute('data-event-id', idString);
domNode.className = 'timer-event-' + (event.id % 2);
this.renderedEvents[idString] = event;
if (this.shouldShow(this.renderedEvents[idString])) {
domNode.style.display = 'inherit';
} else {
domNode.style.display = 'none';
}
this.renderCnt++;
}
private _render(): void {
let allEvents = this.timeKeeper.getCollectedEvents(), didSomething = false;
for (let i = this.lastEventIndex; i < allEvents.length; i++) {
let ev = allEvents[i];
if (!ev.stopTime) {
// This event is not yet finished => block
this.lastEventIndex = i;
if (didSomething) {
this.domNode.scrollTop = 100000;
}
return;
}
this._renderStartTimerEvent(ev);
didSomething = true;
}
if (didSomething) {
this.domNode.scrollTop = 100000;
}
this.lastEventIndex = allEvents.length;
}
private _repeatStr(str: string, cnt: number): string {
let r = '';
for (let i = 0; i < cnt; i++) {
r += str;
}
return r;
}
}
+2 -2
View File
@@ -18,7 +18,7 @@ export function equals<T>(one: T[], other: T[], itemEquals: (a: T, b: T) => bool
return false;
}
for (var i = 0, len = one.length; i < len; i++) {
for (let i = 0, len = one.length; i < len; i++) {
if (!itemEquals(one[i], other[i])) {
return false;
}
@@ -177,7 +177,7 @@ export function first<T>(array: T[], fn: (item: T) => boolean, notFoundValue: T
export function commonPrefixLength<T>(one: T[], other: T[], equals: (a: T, b: T) => boolean = (a, b) => a === b): number {
let result = 0;
for (var i = 0, len = Math.min(one.length, other.length); i < len && equals(one[i], other[i]); i++) {
for (let i = 0, len = Math.min(one.length, other.length); i < len && equals(one[i], other[i]); i++) {
result++;
}
+5 -5
View File
@@ -71,8 +71,8 @@ export interface ITask<T> {
* The throttler implements this via the queue() method, by providing it a task
* factory. Following the example:
*
* var throttler = new Throttler();
* var letters = [];
* const throttler = new Throttler();
* const letters = [];
*
* function deliver() {
* const lettersToDeliver = letters;
@@ -166,8 +166,8 @@ export class SimpleThrottler {
* to be executed and the waiting period (delay) must be passed in as arguments. Following
* the example:
*
* var delayer = new Delayer(WAITING_PERIOD);
* var letters = [];
* const delayer = new Delayer(WAITING_PERIOD);
* const letters = [];
*
* function letterReceived(l) {
* letters.push(l);
@@ -402,7 +402,7 @@ export function sequence<T>(promiseFactories: ITask<TPromise<T>>[]): TPromise<T[
export function first<T>(promiseFactories: ITask<TPromise<T>>[], shouldStop: (t: T) => boolean = t => !!t): TPromise<T> {
promiseFactories = [...promiseFactories.reverse()];
const loop = () => {
const loop: () => TPromise<T> = () => {
if (promiseFactories.length === 0) {
return TPromise.as(null);
}
+3 -3
View File
@@ -65,12 +65,12 @@ function hsla2rgba(hsla: HSLA): RGBA {
let s = Math.min(hsla.s, 1);
let l = Math.min(hsla.l, 1);
let a = hsla.a === void 0 ? hsla.a : 1;
let r, g, b;
let r: number, g: number, b: number;
if (s === 0) {
r = g = b = l; // achromatic
} else {
let hue2rgb = function hue2rgb(p, q, t) {
let hue2rgb = function hue2rgb(p: number, q: number, t: number) {
if (t < 0) {
t += 1;
}
@@ -115,7 +115,7 @@ export class Color {
* Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white.
*/
public getLuminosity(): number {
let luminosityFor = function (color): number {
let luminosityFor = function (color: number): number {
let c = color / 255;
return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4);
};
+2 -2
View File
@@ -83,8 +83,8 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu
}
export interface IScorableResourceAccessor<T> {
getLabel(T): string;
getResourcePath(T): string;
getLabel(t: T): string;
getResourcePath(t: T): string;
}
export function compareByScore<T>(elementA: T, elementB: T, accessor: IScorableResourceAccessor<T>, lookFor: string, lookForNormalizedLower: string, scorerCache?: { [key: string]: number }): number {
-38
View File
@@ -1,38 +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 nls = require('vs/nls');
export function since(date: Date): string {
var seconds = (new Date().getTime() - date.getTime()) / 1000;
if (seconds < 60) {
return nls.localize('diff.seconds.verbose', "just now");
}
var minutes = seconds / 60;
if (minutes < 60) {
return Math.floor(minutes) === 1 ? nls.localize('diff.minute.verbose', "1 minute ago") : nls.localize('diff.minutes.verbose', "{0} minutes ago", Math.floor(minutes));
}
var hours = minutes / 60;
if (hours < 24) {
return Math.floor(hours) === 1 ? nls.localize('diff.hour.verbose', "1 hour ago") : nls.localize('diff.hours.verbose', "{0} hours ago", Math.floor(hours));
}
var days = hours / 24;
if (Math.floor(days) === 1) {
return nls.localize('diff.days.yesterday', "yesterday");
}
if (days > 6 && days < 8) {
return nls.localize('diff.days.week', "a week ago");
}
if (days > 30 && days < 40) {
return nls.localize('diff.days.month', "a month ago");
}
return nls.localize('diff.days.verbose', "{0} days ago", Math.floor(days));
}
+1 -1
View File
@@ -23,7 +23,7 @@ export function memoize(target: any, key: string, descriptor: any) {
const memoizeKey = `$memoize$${key}`;
descriptor[fnKey] = function (...args) {
descriptor[fnKey] = function (...args: any[]) {
if (!this.hasOwnProperty(memoizeKey)) {
Object.defineProperty(this, memoizeKey, {
configurable: false,
+1 -1
View File
@@ -19,7 +19,7 @@ globals.Monaco.Diagnostics = {};
var switches = globals.Monaco.Diagnostics;
var map = {};
var data = [];
var data: any[] = [];
function fifo(array: any[], size: number) {
while (array.length > size) {
+2 -2
View File
@@ -148,8 +148,8 @@ export class LcsDiff2 {
// Construct the changes
let i = 0;
let j = 0;
let xChangeStart, yChangeStart;
let changes = [];
let xChangeStart: number, yChangeStart: number;
let changes: DiffChange[] = [];
while (i < xLength && j < yLength) {
if (this.resultX[i] && this.resultY[j]) {
// No change
+3 -3
View File
@@ -218,7 +218,7 @@ export function once<T>(event: Event<T>): Event<T> {
}
export function any<T>(...events: Event<T>[]): Event<T> {
let listeners = [];
let listeners: IDisposable[] = [];
const emitter = new Emitter<T>({
onFirstListenerAdd() {
@@ -297,7 +297,7 @@ export class EventBufferer {
}
bufferEvents(fn: () => void): void {
const buffer = [];
const buffer: Function[] = [];
this.buffers.push(buffer);
fn();
this.buffers.pop();
@@ -334,7 +334,7 @@ class ChainableEvent<T> implements IChainableEvent<T> {
return new ChainableEvent(filterEvent(this._event, fn));
}
on(listener, thisArgs, disposables) {
on(listener, thisArgs, disposables: IDisposable[]) {
return this._event(listener, thisArgs, disposables);
}
}
+11 -8
View File
@@ -209,7 +209,7 @@ function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis {
}
function isUpperCaseWord(analysis: ICamelCaseAnalysis): boolean {
const { upperPercent, lowerPercent, alphaPercent, numericPercent } = analysis;
const { upperPercent, lowerPercent } = analysis;
return lowerPercent === 0 && upperPercent > 0.6;
}
@@ -272,10 +272,11 @@ export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[]
}
// Matches beginning of words supporting non-ASCII languages
// E.g. "gp" or "g p" will match "Git: Pull"
// If `contiguous` is true then matches word with beginnings of the words in the target. E.g. "pul" will match "Git: Pull"
// Otherwise also matches sub string of the word with beginnings of the words in the target. E.g. "gp" or "g p" will match "Git: Pull"
// Useful in cases where the target is words (e.g. command labels)
export function matchesWords(word: string, target: string): IMatch[] {
export function matchesWords(word: string, target: string, contiguous: boolean = false): IMatch[] {
if (!target || target.length === 0) {
return null;
}
@@ -283,14 +284,14 @@ export function matchesWords(word: string, target: string): IMatch[] {
let result: IMatch[] = null;
let i = 0;
while (i < target.length && (result = _matchesWords(word.toLowerCase(), target, 0, i)) === null) {
while (i < target.length && (result = _matchesWords(word.toLowerCase(), target, 0, i, contiguous)) === null) {
i = nextWord(target, i + 1);
}
return result;
}
function _matchesWords(word: string, target: string, i: number, j: number): IMatch[] {
function _matchesWords(word: string, target: string, i: number, j: number, contiguous: boolean): IMatch[] {
if (i === word.length) {
return [];
} else if (j === target.length) {
@@ -298,13 +299,15 @@ function _matchesWords(word: string, target: string, i: number, j: number): IMat
} else if (word[i] !== target[j].toLowerCase()) {
return null;
} else {
let result = null;
let result: IMatch[] = null;
let nextWordIndex = j + 1;
result = _matchesWords(word, target, i + 1, j + 1);
result = _matchesWords(word, target, i + 1, j + 1, contiguous);
if (!contiguous) {
while (!result && (nextWordIndex = nextWord(target, nextWordIndex)) < target.length) {
result = _matchesWords(word, target, i + 1, nextWordIndex);
result = _matchesWords(word, target, i + 1, nextWordIndex, contiguous);
nextWordIndex++;
}
}
return result === null ? null : join({ start: j, end: j + 1 }, result);
}
}
+1 -1
View File
@@ -42,7 +42,7 @@ export interface JSONScanner {
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number);
setPosition(pos: number): void;
/**
* Read the next token. Returns the tolen code.
*/
+15 -10
View File
@@ -4,8 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { isArray } from './types';
export const empty: IDisposable = Object.freeze({
dispose() { }
});
@@ -14,17 +12,24 @@ export interface IDisposable {
dispose(): void;
}
export function dispose<T extends IDisposable>(...disposables: T[]): T;
export function dispose<T extends IDisposable>(disposable: T): T;
export function dispose<T extends IDisposable>(...disposables: T[]): T[];
export function dispose<T extends IDisposable>(disposables: T[]): T[];
export function dispose<T extends IDisposable>(...disposables: T[]): T[] {
const first = disposables[0];
export function dispose<T extends IDisposable>(first: T | T[], ...rest: T[]): T | T[] {
if (isArray(first)) {
disposables = first as any as T[];
}
disposables.forEach(d => d && d.dispose());
if (Array.isArray(first)) {
first.forEach(d => d && d.dispose());
return [];
} else if (rest.length === 0) {
if (first) {
first.dispose();
return first;
}
} else {
dispose(first);
dispose(rest);
return [];
}
}
export function combinedDisposable(disposables: IDisposable[]): IDisposable {
+4 -4
View File
@@ -52,7 +52,7 @@ export class LinkedMap<K extends Key, T> {
}
public keys(): K[] {
var keys: K[] = [];
const keys: K[] = [];
for (let key in this.map) {
keys.push(this.map[key].key);
}
@@ -60,7 +60,7 @@ export class LinkedMap<K extends Key, T> {
}
public values(): T[] {
var values: T[] = [];
const values: T[] = [];
for (let key in this.map) {
values.push(this.map[key].value);
}
@@ -68,7 +68,7 @@ export class LinkedMap<K extends Key, T> {
}
public entries(): Entry<K, T>[] {
var entries: Entry<K, T>[] = [];
const entries: Entry<K, T>[] = [];
for (let key in this.map) {
entries.push(this.map[key]);
}
@@ -310,7 +310,7 @@ class Node<E> {
*/
export class TrieMap<E> {
static PathSplitter = s => s.split(/[\\/]/).filter(s => !!s);
static PathSplitter = (s: string) => s.split(/[\\/]/).filter(s => !!s);
private _splitter: (s: string) => string[];
private _root = new Node<E>();
+2 -2
View File
@@ -143,7 +143,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText
let patternMatch: ITextMimeAssociationItem;
let extensionMatch: ITextMimeAssociationItem;
for (var i = 0; i < associations.length; i++) {
for (let i = 0; i < associations.length; i++) {
let association = associations[i];
// First exact name match
@@ -243,7 +243,7 @@ export function isUnspecific(mime: string[] | string): boolean {
}
export function suggestFilename(langId: string, prefix: string): string {
for (var i = 0; i < registeredAssociations.length; i++) {
for (let i = 0; i < registeredAssociations.length; i++) {
let association = registeredAssociations[i];
if (association.userConfigured) {
continue; // only support registered ones
+6 -6
View File
@@ -12,23 +12,23 @@ export namespace Schemas {
* A schema that is used for models that exist in memory
* only and that have no correspondence on a server or such.
*/
export var inMemory: string = 'inmemory';
export const inMemory: string = 'inmemory';
/**
* A schema that is used for setting files
*/
export var vscode: string = 'vscode';
export const vscode: string = 'vscode';
/**
* A schema that is used for internal private files
*/
export var internal: string = 'private';
export const internal: string = 'private';
export var http: string = 'http';
export const http: string = 'http';
export var https: string = 'https';
export const https: string = 'https';
export var file: string = 'file';
export const file: string = 'file';
}
export interface IXHROptions {
+5 -5
View File
@@ -11,12 +11,12 @@ import { CharCode } from 'vs/base/common/charCode';
/**
* The forward slash path separator.
*/
export var sep = '/';
export const sep = '/';
/**
* The native path separator depending on the OS.
*/
export var nativeSep = isWindows ? '\\' : '/';
export const nativeSep = isWindows ? '\\' : '/';
export function relative(from: string, to: string): string {
const originalNormalizedFrom = normalize(from);
@@ -50,7 +50,7 @@ export function relative(from: string, to: string): string {
* @returns the directory name of a path.
*/
export function dirname(path: string): string {
var idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\');
const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\');
if (idx === 0) {
return '.';
} else if (~idx === 0) {
@@ -64,7 +64,7 @@ export function dirname(path: string): string {
* @returns the base name of a path.
*/
export function basename(path: string): string {
var idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\');
const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\');
if (idx === 0) {
return path;
} else if (~idx === path.length - 1) {
@@ -79,7 +79,7 @@ export function basename(path: string): string {
*/
export function extname(path: string): string {
path = basename(path);
var idx = ~path.lastIndexOf('.');
const idx = ~path.lastIndexOf('.');
return idx ? path.substring(~idx) : '';
}
+3 -3
View File
@@ -13,8 +13,8 @@ let _isRootUser = false;
let _isNative = false;
let _isWeb = false;
let _isQunit = false;
let _locale = undefined;
let _language = undefined;
let _locale: string = undefined;
let _language: string = undefined;
interface NLSConfig {
locale: string;
@@ -124,7 +124,7 @@ interface IGlobals {
clearTimeout(token: TimeoutToken): void;
setInterval(callback: (...args: any[]) => void, delay: number, ...args: any[]): IntervalToken;
clearInterval(token: IntervalToken);
clearInterval(token: IntervalToken): void;
}
const _globals = <IGlobals>(typeof self === 'object' ? self : global);
+1 -1
View File
@@ -6,7 +6,7 @@
import { globals } from 'vs/base/common/platform';
var hasPerformanceNow = (globals.performance && typeof globals.performance.now === 'function');
const hasPerformanceNow = (globals.performance && typeof globals.performance.now === 'function');
export class StopWatch {
+2 -2
View File
@@ -607,8 +607,8 @@ export function safeBtoa(str: string): string {
}
export function repeat(s: string, count: number): string {
var result = '';
for (var i = 0; i < count; i++) {
let result = '';
for (let i = 0; i < count; i++) {
result += s;
}
return result;
-293
View File
@@ -1,293 +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 Platform = require('vs/base/common/platform');
import errors = require('vs/base/common/errors');
import precision = require('vs/base/common/stopwatch');
import { IDisposable } from 'vs/base/common/lifecycle';
export var ENABLE_TIMER = false;
var msWriteProfilerMark = Platform.globals['msWriteProfilerMark'];
export enum Topic {
EDITOR,
LANGUAGES,
WORKER,
WORKBENCH,
STARTUP
}
export interface ITimerEvent {
id: number;
topic: string;
name: string;
description: string;
data: any;
startTime: Date;
stopTime: Date;
stop(stopTime?: Date): void;
timeTaken(): number;
}
export interface IExistingTimerEvent {
topic: string;
name: string;
description?: string;
startTime: Date;
stopTime: Date;
}
class NullTimerEvent implements ITimerEvent {
public id: number;
public topic: string;
public name: string;
public description: string;
public data: any;
public startTime: Date;
public stopTime: Date;
public stop(): void {
return;
}
public timeTaken(): number {
return -1;
}
}
class TimerEvent implements ITimerEvent {
public id: number;
public topic: string;
public name: string;
public description: string;
public data: any;
public startTime: Date;
public stopTime: Date;
private timeKeeper: TimeKeeper;
private sw: precision.StopWatch;
constructor(timeKeeper: TimeKeeper, name: string, topic: string, startTime?: Date, description?: string) {
this.timeKeeper = timeKeeper;
this.name = name;
this.description = description;
this.topic = topic;
this.stopTime = null;
if (startTime) {
this.startTime = startTime;
return;
}
this.startTime = new Date();
this.sw = precision.StopWatch.create();
if (msWriteProfilerMark) {
var profilerName = ['Monaco', this.topic, this.name, 'start'];
msWriteProfilerMark(profilerName.join('|'));
}
}
public stop(stopTime?: Date): void {
// already stopped
if (this.stopTime !== null) {
return;
}
if (stopTime) {
this.stopTime = stopTime;
this.sw = null;
this.timeKeeper._onEventStopped(this);
return;
}
this.stopTime = new Date();
if (this.sw) {
this.sw.stop();
}
this.timeKeeper._onEventStopped(this);
if (msWriteProfilerMark) {
var profilerName = ['Monaco', this.topic, this.name, 'stop'];
msWriteProfilerMark(profilerName.join('|'));
}
}
public timeTaken(): number {
if (this.sw) {
return this.sw.elapsed();
}
if (this.stopTime) {
return this.stopTime.getTime() - this.startTime.getTime();
}
return -1;
}
}
export interface IEventsListener {
(events: ITimerEvent[]): void;
}
export class TimeKeeper {
/**
* After being started for 1 minute, all timers are automatically stopped.
*/
private static _MAX_TIMER_LENGTH = 60000; // 1 minute
/**
* Every 2 minutes, a sweep of current started timers is done.
*/
private static _CLEAN_UP_INTERVAL = 120000; // 2 minutes
/**
* Collect at most 1000 events.
*/
private static _EVENT_CACHE_LIMIT = 1000;
private static EVENT_ID = 1;
public static PARSE_TIME = new Date();
private cleaningIntervalId: Platform.IntervalToken;
private collectedEvents: ITimerEvent[];
private listeners: IEventsListener[];
constructor() {
this.cleaningIntervalId = -1;
this.collectedEvents = [];
this.listeners = [];
}
public isEnabled(): boolean {
return ENABLE_TIMER;
}
public start(topic: Topic | string, name: string, start?: Date, description?: string): ITimerEvent {
if (!this.isEnabled()) {
return nullEvent;
}
var strTopic: string;
if (typeof topic === 'string') {
strTopic = topic;
} else if (topic === Topic.EDITOR) {
strTopic = 'Editor';
} else if (topic === Topic.LANGUAGES) {
strTopic = 'Languages';
} else if (topic === Topic.WORKER) {
strTopic = 'Worker';
} else if (topic === Topic.WORKBENCH) {
strTopic = 'Workbench';
} else if (topic === Topic.STARTUP) {
strTopic = 'Startup';
}
this.initAutoCleaning();
var event = new TimerEvent(this, name, strTopic, start, description);
this.addEvent(event);
return event;
}
public dispose(): void {
if (this.cleaningIntervalId !== -1) {
Platform.clearInterval(this.cleaningIntervalId);
this.cleaningIntervalId = -1;
}
}
public addListener(listener: IEventsListener): IDisposable {
this.listeners.push(listener);
return {
dispose: () => {
for (var i = 0; i < this.listeners.length; i++) {
if (this.listeners[i] === listener) {
this.listeners.splice(i, 1);
return;
}
}
}
};
}
private addEvent(event: ITimerEvent): void {
event.id = TimeKeeper.EVENT_ID;
TimeKeeper.EVENT_ID++;
this.collectedEvents.push(event);
// expire items from the front of the cache
if (this.collectedEvents.length > TimeKeeper._EVENT_CACHE_LIMIT) {
this.collectedEvents.shift();
}
}
private initAutoCleaning(): void {
if (this.cleaningIntervalId === -1) {
this.cleaningIntervalId = Platform.setInterval(() => {
var now = Date.now();
this.collectedEvents.forEach((event) => {
if (!event.stopTime && (now - event.startTime.getTime()) >= TimeKeeper._MAX_TIMER_LENGTH) {
event.stop();
}
});
}, TimeKeeper._CLEAN_UP_INTERVAL);
}
}
public getCollectedEvents(): ITimerEvent[] {
return this.collectedEvents.slice(0);
}
public clearCollectedEvents(): void {
this.collectedEvents = [];
}
_onEventStopped(event: ITimerEvent): void {
var emitEvents = [event];
var listeners = this.listeners.slice(0);
for (var i = 0; i < listeners.length; i++) {
try {
listeners[i](emitEvents);
} catch (e) {
errors.onUnexpectedError(e);
}
}
}
public setInitialCollectedEvents(events: IExistingTimerEvent[], startTime?: Date): void {
if (!this.isEnabled()) {
return;
}
if (startTime) {
TimeKeeper.PARSE_TIME = startTime;
}
events.forEach((event) => {
var e = new TimerEvent(this, event.name, event.topic, event.startTime, event.description);
e.stop(event.stopTime);
this.addEvent(e);
});
}
}
var timeKeeper = new TimeKeeper();
export var nullEvent: ITimerEvent = new NullTimerEvent();
export function start(topic: Topic | string, name: string, start?: Date, description?: string): ITimerEvent {
return timeKeeper.start(topic, name, start, description);
}
export function getTimeKeeper(): TimeKeeper {
return timeKeeper;
}
+1 -1
View File
@@ -106,7 +106,7 @@ function read(zipPath: string, filePath: string): TPromise<Readable> {
export function buffer(zipPath: string, filePath: string): TPromise<Buffer> {
return read(zipPath, filePath).then(stream => {
return new TPromise<Buffer>((c, e) => {
const buffers = [];
const buffers: Buffer[] = [];
stream.once('error', e);
stream.on('data', b => buffers.push(b));
stream.on('end', () => c(Buffer.concat(buffers)));
@@ -689,15 +689,15 @@ export class QuickOpenModel implements
return this._entries;
}
getId(entry: QuickOpenEntry): string {
public getId(entry: QuickOpenEntry): string {
return entry.getId();
}
getLabel(entry: QuickOpenEntry): string {
public getLabel(entry: QuickOpenEntry): string {
return entry.getLabel();
}
getAriaLabel(entry: QuickOpenEntry): string {
public getAriaLabel(entry: QuickOpenEntry): string {
const ariaLabel = entry.getAriaLabel();
if (ariaLabel) {
return nls.localize('quickOpenAriaLabelEntry', "{0}, picker", entry.getAriaLabel());
@@ -706,11 +706,11 @@ export class QuickOpenModel implements
return nls.localize('quickOpenAriaLabel', "picker");
}
isVisible(entry: QuickOpenEntry): boolean {
public isVisible(entry: QuickOpenEntry): boolean {
return !entry.isHidden();
}
run(entry: QuickOpenEntry, mode: Mode, context: IContext): boolean {
public run(entry: QuickOpenEntry, mode: Mode, context: IContext): boolean {
return entry.run(mode, context);
}
}
+1 -1
View File
@@ -328,7 +328,7 @@ export interface ITree extends Events.IEventEmitter {
* Returns a navigator which allows to discover the visible and
* expanded elements in the tree.
*/
getNavigator(): INavigator<any>;
getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator<any>;
/**
* Disposes the tree
+3 -3
View File
@@ -133,7 +133,7 @@ export class Tree extends Events.EventEmitter implements _.ITree {
}
public collapse(element: any, recursive: boolean = false): WinJS.Promise {
return this.model.collapse(element);
return this.model.collapse(element, recursive);
}
public collapseAll(elements: any[] = null, recursive: boolean = false): WinJS.Promise {
@@ -318,8 +318,8 @@ export class Tree extends Events.EventEmitter implements _.ITree {
return this.model.hasTrait(trait, element);
}
getNavigator(): INavigator<any> {
return new MappedNavigator(this.model.getNavigator(), i => i && i.getElement());
getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator<any> {
return new MappedNavigator(this.model.getNavigator(fromElement, subTreeOnly), i => i && i.getElement());
}
public dispose(): void {
+3
View File
@@ -186,5 +186,8 @@ suite('Filters', () => {
filterOk(matchesWords, 'git プル', 'git: プル', [{ start: 0, end: 3 }, { start: 4, end: 7 }]);
filterOk(matchesWords, 'öäk', 'Öhm: Älles Klar', [{ start: 0, end: 1 }, { start: 5, end: 6 }, { start: 11, end: 12 }]);
assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null);
assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]);
});
});
+14 -5
View File
@@ -13,18 +13,21 @@ import URI from 'vs/base/common/uri';
export class DeferredTPromise<T> extends TPromise<T> {
public canceled = false;
public canceled: boolean;
private completeCallback: TValueCallback<T>;
private errorCallback: (err: any) => void;
private progressCallback: ProgressCallback;
constructor() {
let captured: any;
super((c, e, p) => {
this.completeCallback = c;
this.errorCallback = e;
this.progressCallback = p;
captured = { c, e, p };
}, () => this.oncancel());
this.canceled = false;
this.completeCallback = captured.c;
this.errorCallback = captured.e;
this.progressCallback = captured.p;
}
public complete(value: T) {
@@ -51,7 +54,13 @@ export class DeferredPPromise<C, P> extends PPromise<C, P> {
private progressCallback: TProgressCallback<P>;
constructor(init: (complete: TValueCallback<C>, error: (err: any) => void, progress: TProgressCallback<P>) => void = (c, e, p) => { }, oncancel?: any) {
super((c, e, p) => { this.completeCallback = c; this.errorCallback = e; this.progressCallback = p; }, oncancel ? oncancel : () => this.oncancel);
let captured: any;
super((c, e, p) => {
captured = { c, e, p };
}, oncancel ? oncancel : () => this.oncancel);
this.completeCallback = captured.c;
this.errorCallback = captured.e;
this.progressCallback = captured.p;
}
private oncancel(): void {
+9 -6
View File
@@ -5,7 +5,7 @@
'use strict';
import { IWindowsMainService } from 'vs/code/electron-main/windows';
import { IWindowsMainService, OpenContext } from 'vs/code/electron-main/windows';
import { VSCodeWindow } from 'vs/code/electron-main/window';
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
@@ -82,6 +82,7 @@ export class LaunchService implements ILaunchService {
const openUrlArg = args['open-url'] || [];
const openUrl = typeof openUrlArg === 'string' ? [openUrlArg] : openUrlArg;
const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.OTHER;
if (openUrl.length > 0) {
openUrl.forEach(url => this.urlService.open(url));
@@ -91,17 +92,19 @@ export class LaunchService implements ILaunchService {
// Otherwise handle in windows service
let usedWindows: VSCodeWindow[];
if (!!args.extensionDevelopmentPath) {
this.windowsService.openExtensionDevelopmentHostWindow({ cli: args, userEnv });
} else if (args._.length === 0 && args['new-window']) {
usedWindows = this.windowsService.open({ cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
this.windowsService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv });
} else if (args._.length === 0 && args['new-window'] || args['new-window-if-not-first']) {
usedWindows = this.windowsService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
} else if (args._.length === 0) {
usedWindows = [this.windowsService.focusLastActive(args)];
usedWindows = [this.windowsService.focusLastActive(args, context)];
} else {
usedWindows = this.windowsService.open({
context,
cli: args,
userEnv,
forceNewWindow: args.wait || args['new-window'],
forceNewWindow: args.wait || args['new-window'] || args['new-window-if-not-first'],
preferNewWindow: !args['reuse-window'],
forceReuseWindow: args['reuse-window'],
diffMode: args.diff
});
}
+5 -4
View File
@@ -11,7 +11,7 @@ import * as platform from 'vs/base/common/platform';
import { parseMainProcessArgv } from 'vs/platform/environment/node/argv';
import { mkdirp } from 'vs/base/node/pfs';
import { validatePaths } from 'vs/code/electron-main/paths';
import { IWindowsMainService, WindowsManager } from 'vs/code/electron-main/windows';
import { IWindowsMainService, WindowsManager, OpenContext } from 'vs/code/electron-main/windows';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
@@ -251,12 +251,13 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
windowsMainService.ready(userEnv);
// Open our first window
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.OTHER;
if (environmentService.args['new-window'] && environmentService.args._.length === 0) {
windowsMainService.open({ cli: environmentService.args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
} else if (global.macOpenFiles && global.macOpenFiles.length && (!environmentService.args._ || !environmentService.args._.length)) {
windowsMainService.open({ cli: environmentService.args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup
windowsMainService.open({ context: OpenContext.DOCK, cli: environmentService.args, pathsToOpen: global.macOpenFiles, initialStartup: true }); // mac: open-file event received on startup
} else {
windowsMainService.open({ cli: environmentService.args, forceNewWindow: environmentService.args['new-window'], diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli
windowsMainService.open({ context, cli: environmentService.args, forceNewWindow: environmentService.args['new-window'] || environmentService.args['new-window-if-not-first'], diffMode: environmentService.args.diff, initialStartup: true }); // default: read paths from cli
}
// Install Menu
+29 -13
View File
@@ -10,7 +10,7 @@ import * as platform from 'vs/base/common/platform';
import * as arrays from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem } from 'electron';
import { IWindowsMainService } from 'vs/code/electron-main/windows';
import { IWindowsMainService, OpenContext } from 'vs/code/electron-main/windows';
import { VSCodeWindow } from 'vs/code/electron-main/window';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageService } from 'vs/code/electron-main/storage';
@@ -315,7 +315,7 @@ export class VSCodeMenu {
this.appMenuInstalled = true;
const dockMenu = new Menu();
dockMenu.append(new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow() }));
dockMenu.append(new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.DOCK) }));
app.dock.setMenu(dockMenu);
}
@@ -351,19 +351,19 @@ export class VSCodeMenu {
let newFile: Electron.MenuItem;
if (hasNoWindows) {
newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File")), click: () => this.windowsService.openNewWindow() }));
newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) }));
} else {
newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File"), 'workbench.action.files.newUntitledFile');
}
const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: () => this.windowsService.openFileFolderPicker() }));
const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: () => this.windowsService.openFolderPicker() }));
const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.openFileFolderPicker(this.isOptionClick(event)) }));
const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.openFolderPicker(this.isOptionClick(event)) }));
let openFile: Electron.MenuItem;
if (hasNoWindows) {
openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: () => this.windowsService.openFilePicker() }));
openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: (menuItem, win, event) => this.windowsService.openFilePicker(this.isOptionClick(event)) }));
} else {
openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), 'workbench.action.files.openFile');
openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), ['workbench.action.files.openFile', 'workbench.action.files.openFileInNewWindow']);
}
const openRecentMenu = new Menu();
@@ -379,7 +379,7 @@ export class VSCodeMenu {
const preferences = this.getPreferencesMenu();
const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow() }));
const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) }));
const revertFile = this.createMenuItem(nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), 'workbench.action.files.revert', this.windowsService.getWindowCount() > 0);
const closeWindow = new MenuItem(this.likeAction('workbench.action.closeWindow', { label: mnemonicLabel(nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")), click: () => this.windowsService.getLastActiveWindow().win.close(), enabled: this.windowsService.getWindowCount() > 0 }));
@@ -468,10 +468,15 @@ export class VSCodeMenu {
}
private createOpenRecentMenuItem(path: string, actionId: string): Electron.MenuItem {
let label = path;
if ((platform.isMacintosh || platform.isLinux) && path.indexOf(this.environmentService.userHome) === 0) {
label = `~${path.substr(this.environmentService.userHome.length)}`;
}
return new MenuItem(this.likeAction(actionId, {
label: unMnemonicLabel(path), click: (menuItem, win, event) => {
const openInNewWindow = event && ((!platform.isMacintosh && event.ctrlKey) || (platform.isMacintosh && event.metaKey));
const success = !!this.windowsService.open({ cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow });
label: unMnemonicLabel(label), click: (menuItem, win, event) => {
const openInNewWindow = this.isOptionClick(event);
const success = !!this.windowsService.open({ context: OpenContext.MENU, cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow });
if (!success) {
this.windowsService.removeFromRecentPathsList(path);
}
@@ -479,6 +484,10 @@ export class VSCodeMenu {
}, false));
}
private isOptionClick(event: Electron.Event): boolean {
return event && ((!platform.isMacintosh && (event.ctrlKey || event.shiftKey)) || (platform.isMacintosh && (event.metaKey || event.altKey)));
}
private createRoleMenuItem(label: string, actionId: string, role: Electron.MenuItemRole): Electron.MenuItem {
const options: Electron.MenuItemOptions = {
label: mnemonicLabel(label),
@@ -890,11 +899,18 @@ export class VSCodeMenu {
}
}
private createMenuItem(label: string, actionId: string, enabled?: boolean, checked?: boolean): Electron.MenuItem;
private createMenuItem(label: string, actionId: string | string[], enabled?: boolean, checked?: boolean): Electron.MenuItem;
private createMenuItem(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem;
private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem {
const label = mnemonicLabel(arg1);
const click: () => void = (typeof arg2 === 'function') ? arg2 : () => this.windowsService.sendToFocused('vscode:runAction', arg2);
const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem, win, event) => {
let actionId = arg2;
if (Array.isArray(arg2)) {
actionId = this.isOptionClick(event) ? arg2[1] : arg2[0]; // support alternative action if we got multiple action Ids and the option key was pressed while invoking
}
this.windowsService.sendToFocused('vscode:runAction', actionId);
};
const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0;
const checked = typeof arg4 === 'boolean' ? arg4 : false;
+22 -16
View File
@@ -82,6 +82,7 @@ export interface IWindowConfiguration extends ParsedArgs {
isInitialStartup?: boolean;
perfStartTime?: number;
perfAppReady?: number;
perfWindowLoadTime?: number;
workspacePath?: string;
@@ -205,21 +206,6 @@ export class VSCodeWindow implements IVSCodeWindow {
this._win = new BrowserWindow(options);
this._id = this._win.id;
// TODO@joao: hook this up to some initialization routine
// this causes a race between setting the headers and doing
// a request that needs them. chances are low
getCommonHTTPHeaders().done(headers => {
if (!this._win) {
return;
}
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => {
cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) });
});
});
if (isFullscreenOrMaximized) {
this.win.maximize();
@@ -238,9 +224,28 @@ export class VSCodeWindow implements IVSCodeWindow {
this.setMenuBarVisibility(false); // respect configured menu bar visibility
}
// TODO@joao: hook this up to some initialization routine
// this causes a race between setting the headers and doing
// a request that needs them. chances are low
this.setCommonHTTPHeaders();
this.registerListeners();
}
private setCommonHTTPHeaders(): void {
getCommonHTTPHeaders().done(headers => {
if (!this._win) {
return;
}
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => {
cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) });
});
});
}
public hasHiddenTitleBarStyle(): boolean {
return this.hiddenTitleBarStyle;
}
@@ -478,11 +483,12 @@ export class VSCodeWindow implements IVSCodeWindow {
windowConfiguration.fullscreen = this._win.isFullScreen();
// Set Accessibility Config
windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme();
windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme() && (!windowConfig || windowConfig.autoDetectHighContrast);
windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled();
// Perf Counters
windowConfiguration.perfStartTime = global.perfStartTime;
windowConfiguration.perfAppReady = global.perfAppReady;
windowConfiguration.perfWindowLoadTime = Date.now();
// Config (combination of process.argv and window configuration)
+61 -28
View File
@@ -34,12 +34,32 @@ enum WindowError {
CRASHED
}
export enum OpenContext {
// opening when running from the command line
CLI,
// macOS only: opening from the dock (also when opening files to a running instance from desktop)
DOCK,
// opening from the main application window
MENU,
// opening from a file or folder dialog
DIALOG,
// any other way of opening
OTHER
}
export interface IOpenConfiguration {
context: OpenContext;
cli: ParsedArgs;
userEnv?: platform.IProcessEnvironment;
pathsToOpen?: string[];
preferNewWindow?: boolean;
forceNewWindow?: boolean;
forceReuseWindow?: boolean;
forceEmpty?: boolean;
windowToUse?: VSCodeWindow;
diffMode?: boolean;
@@ -96,10 +116,10 @@ export interface IWindowsMainService {
openFilePicker(forceNewWindow?: boolean, path?: string, window?: VSCodeWindow): void;
openFolderPicker(forceNewWindow?: boolean, window?: VSCodeWindow): void;
openAccessibilityOptions(): void;
focusLastActive(cli: ParsedArgs): VSCodeWindow;
focusLastActive(cli: ParsedArgs, context: OpenContext): VSCodeWindow;
getLastActiveWindow(): VSCodeWindow;
findWindow(workspacePath: string, filePath?: string, extensionDevelopmentPath?: string): VSCodeWindow;
openNewWindow(): void;
openNewWindow(context: OpenContext): void;
sendToFocused(channel: string, ...args: any[]): void;
sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void;
getFocusedWindow(): VSCodeWindow;
@@ -166,7 +186,7 @@ export class WindowsManager implements IWindowsMainService {
// Mac only event: open new window when we get activated
if (!hasVisibleWindows) {
this.openNewWindow();
this.openNewWindow(OpenContext.DOCK);
}
});
@@ -187,7 +207,12 @@ export class WindowsManager implements IWindowsMainService {
// Handle paths delayed in case more are coming!
runningTimeout = setTimeout(() => {
this.open({ cli: this.environmentService.args, pathsToOpen: macOpenFiles, preferNewWindow: true /* dropping on the dock prefers to open in a new window */ });
this.open({
context: OpenContext.DOCK /* can also be opening from finder while app is running */,
cli: this.environmentService.args,
pathsToOpen: macOpenFiles,
preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
});
macOpenFiles = [];
runningTimeout = null;
}, 100);
@@ -272,6 +297,8 @@ export class WindowsManager implements IWindowsMainService {
}
public open(openConfig: IOpenConfiguration): VSCodeWindow[] {
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
let iPathsToOpen: IPath[];
const usedWindows: VSCodeWindow[] = [];
@@ -345,22 +372,26 @@ export class WindowsManager implements IWindowsMainService {
filesToOpen = candidates;
}
let openInNewWindow = openConfig.preferNewWindow || openConfig.forceNewWindow;
// let the user settings override how folders are open in a new window or same window unless we are forced
let openFolderInNewWindow = (openConfig.preferNewWindow || openConfig.forceNewWindow) && !openConfig.forceReuseWindow;
if (!openConfig.forceNewWindow && !openConfig.forceReuseWindow && windowConfig && (windowConfig.openFoldersInNewWindow === 'on' || windowConfig.openFoldersInNewWindow === 'off')) {
openFolderInNewWindow = (windowConfig.openFoldersInNewWindow === 'on');
}
// Handle files to open/diff or to create when we dont open a folder
if (!foldersToOpen.length && (filesToOpen.length > 0 || filesToCreate.length > 0 || filesToDiff.length > 0)) {
// const the user settings override how files are open in a new window or same window unless we are forced
// let the user settings override how files are open in a new window or same window unless we are forced (not for extension development though)
let openFilesInNewWindow: boolean;
if (openConfig.forceNewWindow) {
openFilesInNewWindow = true;
if (openConfig.forceNewWindow || openConfig.forceReuseWindow) {
openFilesInNewWindow = openConfig.forceNewWindow && !openConfig.forceReuseWindow;
} else {
openFilesInNewWindow = openConfig.preferNewWindow;
if (openFilesInNewWindow && !openConfig.cli.extensionDevelopmentPath) { // can be overriden via settings (not for PDE though!)
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
if (windowConfig && !windowConfig.openFilesInNewWindow) {
openFilesInNewWindow = false; // do not open in new window if user configured this explicitly
if (openConfig.context === OpenContext.DOCK) {
openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context
}
if (!openConfig.cli.extensionDevelopmentPath && windowConfig && (windowConfig.openFilesInNewWindow === 'on' || windowConfig.openFilesInNewWindow === 'off' || <any>windowConfig.openFilesInNewWindow === false /* TODO@Ben migration */)) {
openFilesInNewWindow = (windowConfig.openFilesInNewWindow === 'on');
}
}
@@ -368,8 +399,9 @@ export class WindowsManager implements IWindowsMainService {
const lastActiveWindow = this.getLastActiveWindow();
if (!openFilesInNewWindow && lastActiveWindow) {
lastActiveWindow.focus();
const files = { filesToOpen, filesToCreate, filesToDiff }; // copy to object because they get reset shortly after
lastActiveWindow.ready().then(readyWindow => {
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff });
readyWindow.send('vscode:openFiles', files);
});
usedWindows.push(lastActiveWindow);
@@ -381,7 +413,7 @@ export class WindowsManager implements IWindowsMainService {
const browserWindow = this.openInBrowserWindow(configuration, true /* new window */);
usedWindows.push(browserWindow);
openInNewWindow = true; // any other folders to open must open in new window then
openFolderInNewWindow = true; // any other folders to open must open in new window then
}
// Reset these because we handled them
@@ -399,8 +431,9 @@ export class WindowsManager implements IWindowsMainService {
if (windowsOnWorkspacePath.length > 0) {
const browserWindow = windowsOnWorkspacePath[0];
browserWindow.focus(); // just focus one of them
const files = { filesToOpen, filesToCreate, filesToDiff }; // copy to object because they get reset shortly after
browserWindow.ready().then(readyWindow => {
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff });
readyWindow.send('vscode:openFiles', files);
});
usedWindows.push(browserWindow);
@@ -410,7 +443,7 @@ export class WindowsManager implements IWindowsMainService {
filesToCreate = [];
filesToDiff = [];
openInNewWindow = true; // any other folders to open must open in new window then
openFolderInNewWindow = true; // any other folders to open must open in new window then
}
// Open remaining ones
@@ -420,7 +453,7 @@ export class WindowsManager implements IWindowsMainService {
}
const configuration = this.toConfiguration(openConfig, folderToOpen, filesToOpen, filesToCreate, filesToDiff);
const browserWindow = this.openInBrowserWindow(configuration, openInNewWindow, openInNewWindow ? void 0 : openConfig.windowToUse);
const browserWindow = this.openInBrowserWindow(configuration, openFolderInNewWindow, openFolderInNewWindow ? void 0 : openConfig.windowToUse);
usedWindows.push(browserWindow);
// Reset these because we handled them
@@ -428,7 +461,7 @@ export class WindowsManager implements IWindowsMainService {
filesToCreate = [];
filesToDiff = [];
openInNewWindow = true; // any other folders to open must open in new window then
openFolderInNewWindow = true; // any other folders to open must open in new window then
});
}
@@ -439,7 +472,7 @@ export class WindowsManager implements IWindowsMainService {
const browserWindow = this.openInBrowserWindow(configuration, true /* new window */, null, emptyWorkspaceBackupFolder);
usedWindows.push(browserWindow);
openInNewWindow = true; // any other folders to open must open in new window then
openFolderInNewWindow = true; // any other folders to open must open in new window then
});
}
@@ -447,10 +480,10 @@ export class WindowsManager implements IWindowsMainService {
else if (emptyToOpen.length > 0) {
emptyToOpen.forEach(() => {
const configuration = this.toConfiguration(openConfig);
const browserWindow = this.openInBrowserWindow(configuration, openInNewWindow, openInNewWindow ? void 0 : openConfig.windowToUse);
const browserWindow = this.openInBrowserWindow(configuration, openFolderInNewWindow, openFolderInNewWindow ? void 0 : openConfig.windowToUse);
usedWindows.push(browserWindow);
openInNewWindow = true; // any other folders to open must open in new window then
openFolderInNewWindow = true; // any other folders to open must open in new window then
});
}
@@ -609,7 +642,7 @@ export class WindowsManager implements IWindowsMainService {
}
// Open it
this.open({ cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0 });
this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0 });
}
private toConfiguration(config: IOpenConfiguration, workspacePath?: string, filesToOpen?: IPath[], filesToCreate?: IPath[], filesToDiff?: IPath[]): IWindowConfiguration {
@@ -889,7 +922,7 @@ export class WindowsManager implements IWindowsMainService {
private doPickAndOpen(options: INativeOpenDialogOptions): void {
this.getFileOrFolderPaths(options, (paths: string[]) => {
if (paths && paths.length) {
this.open({ cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: options.forceNewWindow });
this.open({ context: OpenContext.DIALOG, cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: options.forceNewWindow });
}
});
}
@@ -922,7 +955,7 @@ export class WindowsManager implements IWindowsMainService {
});
}
public focusLastActive(cli: ParsedArgs): VSCodeWindow {
public focusLastActive(cli: ParsedArgs, context: OpenContext): VSCodeWindow {
const lastActive = this.getLastActiveWindow();
if (lastActive) {
lastActive.focus();
@@ -932,7 +965,7 @@ export class WindowsManager implements IWindowsMainService {
// No window - open new one
this.windowsState.openedFolders = []; // make sure we do not open too much
const res = this.open({ cli: cli });
const res = this.open({ context, cli });
return res && res[0];
}
@@ -994,8 +1027,8 @@ export class WindowsManager implements IWindowsMainService {
return null;
}
public openNewWindow(): void {
this.open({ cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
public openNewWindow(context: OpenContext): void {
this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
}
public sendToFocused(channel: string, ...args: any[]): void {
+7 -86
View File
@@ -41,41 +41,6 @@ class CSSBasedConfigurationCache {
}
}
class CharWidthReader {
private _chr: string;
private _width: number;
public get width(): number { return this._width; }
constructor(chr: string) {
this._chr = chr;
this._width = 0;
}
public render(out: HTMLSpanElement): void {
if (this._chr === ' ') {
let htmlString = '&nbsp;';
// Repeat character 256 (2^8) times
for (let i = 0; i < 8; i++) {
htmlString += htmlString;
}
out.innerHTML = htmlString;
} else {
let testString = this._chr;
// Repeat character 256 (2^8) times
for (let i = 0; i < 8; i++) {
testString += testString;
}
out.textContent = testString;
}
}
public read(out: HTMLSpanElement): void {
this._width = out.offsetWidth / 256;
}
}
class CSSBasedConfiguration extends Disposable {
public static INSTANCE = new CSSBasedConfiguration();
@@ -154,59 +119,15 @@ class CSSBasedConfiguration extends Disposable {
}
}
private static _testElementId(index: number): string {
return 'editorSizeProvider' + index;
}
private static _createTestElements(bareFontInfo: BareFontInfo, readers: CharWidthReader[]): HTMLElement {
let container = document.createElement('div');
Configuration.applyFontInfoSlow(container, bareFontInfo);
container.style.position = 'absolute';
container.style.top = '-50000px';
container.style.width = '50000px';
for (let i = 0, len = readers.length; i < len; i++) {
container.appendChild(document.createElement('br'));
let testElement = document.createElement('span');
testElement.id = this._testElementId(i);
readers[i].render(testElement);
container.appendChild(testElement);
}
container.appendChild(document.createElement('br'));
return container;
}
private static _readFromTestElements(readers: CharWidthReader[]): void {
for (let i = 0, len = readers.length; i < len; i++) {
readers[i].read(document.getElementById(this._testElementId(i)));
}
}
private static _runReaders(bareFontInfo: BareFontInfo, readers: CharWidthReader[]): void {
// Create a test container with all these test elements
let testContainer = this._createTestElements(bareFontInfo, readers);
// Add the container to the DOM
document.body.appendChild(testContainer);
// Read various properties
this._readFromTestElements(readers);
// Remove the container from the DOM
document.body.removeChild(testContainer);
}
private static _actualReadConfiguration(bareFontInfo: BareFontInfo): FontInfo {
let typicalHalfwidthCharacter = new CharWidthReader('n');
let typicalFullwidthCharacter = new CharWidthReader('\uff4d');
let space = new CharWidthReader(' ');
let digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].map(chr => new CharWidthReader(chr));
let canvasElem = <HTMLCanvasElement>document.createElement('canvas');
let context = canvasElem.getContext('2d');
context.font = `normal normal normal normal ${bareFontInfo.fontSize}px / ${bareFontInfo.lineHeight}px ${bareFontInfo.fontFamily}`;
this._runReaders(bareFontInfo, digits.concat([typicalHalfwidthCharacter, typicalFullwidthCharacter, space]));
let typicalHalfwidthCharacter = context.measureText('n');
let typicalFullwidthCharacter = context.measureText('\uff4d');
let space = context.measureText(' ');
let digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].map(chr => context.measureText(chr));
let maxDigitWidth = 0;
for (let i = 0, len = digits.length; i < len; i++) {
-5
View File
@@ -7,7 +7,6 @@
import { onUnexpectedError } from 'vs/base/common/errors';
import { EmitterEvent, IEventEmitter } from 'vs/base/common/eventEmitter';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as timer from 'vs/base/common/timer';
import * as browser from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
import { StyleMutator } from 'vs/base/browser/styleMutator';
@@ -899,14 +898,12 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
if (!dom.isInDOM(this.domNode)) {
return;
}
let t = timer.start(timer.Topic.EDITOR, 'View.render');
let viewPartsToRender = this._getViewPartsToRender();
if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) {
// Nothing to render
this.keyboardHandler.writeToTextArea();
t.stop();
return;
}
@@ -940,8 +937,6 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
// Render the scrollbar
this.layoutProvider.renderScrollbar();
t.stop();
}
private _setHasFocus(newHasFocus: boolean): void {
@@ -598,50 +598,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
protected abstract readConfiguration(styling: editorCommon.BareFontInfo): editorCommon.FontInfo;
}
/**
* Helper to update Monaco Editor Settings from configurations service.
*/
export class EditorConfiguration {
public static EDITOR_SECTION = 'editor';
public static DIFF_EDITOR_SECTION = 'diffEditor';
/**
* Ask the provided configuration service to apply its configuration to the provided editor.
*/
public static apply(config: any, editor: editorCommon.IEditor): void {
if (!config) {
return;
}
// Editor Settings (Code Editor, Diff, Terminal)
if (editor && typeof editor.updateOptions === 'function') {
let type = editor.getEditorType();
if (type !== editorCommon.EditorType.ICodeEditor && type !== editorCommon.EditorType.IDiffEditor) {
return;
}
let editorConfig = config[EditorConfiguration.EDITOR_SECTION];
if (type === editorCommon.EditorType.IDiffEditor) {
let diffEditorConfig = config[EditorConfiguration.DIFF_EDITOR_SECTION];
if (diffEditorConfig) {
if (!editorConfig) {
editorConfig = diffEditorConfig;
} else {
editorConfig = objects.mixin(editorConfig, diffEditorConfig);
}
}
}
if (editorConfig) {
delete editorConfig.readOnly; // Prevent someone from making editor readonly
editor.updateOptions(editorConfig);
}
}
}
}
let configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
let editorConfiguration: IConfigurationNode = {
const configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
const editorConfiguration: IConfigurationNode = {
'id': 'editor',
'order': 5,
'type': 'object',
@@ -8,7 +8,6 @@ import * as nls from 'vs/nls';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import { StopWatch } from 'vs/base/common/stopwatch';
import * as timer from 'vs/base/common/timer';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { TextModel } from 'vs/editor/common/model/textModel';
@@ -294,7 +293,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
this._withModelTokensChangedEventBuilder((eventBuilder) => {
var t1 = timer.start(timer.Topic.EDITOR, 'backgroundTokenization');
toLineNumber = Math.min(this._lines.length, toLineNumber);
var MAX_ALLOWED_TIME = 20,
@@ -342,8 +340,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
if (this._invalidLineStartIndex < this._lines.length) {
this._beginBackgroundTokenization();
}
t1.stop();
});
}
+1 -1
View File
@@ -125,7 +125,7 @@ const enum CharacterClass {
}
const classifier = (function () {
let result = new CharacterClassifier(CharacterClass.None);
let result = new CharacterClassifier<CharacterClass>(CharacterClass.None);
const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…';
for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) {
+9 -6
View File
@@ -34,7 +34,7 @@ class ChangeRecorder {
private _fileService: IFileService;
constructor(fileService: IFileService) {
constructor(fileService?: IFileService) {
this._fileService = fileService;
}
@@ -42,7 +42,9 @@ class ChangeRecorder {
const changes: IStringDictionary<IFileChange[]> = Object.create(null);
const stop = this._fileService.onFileChanges((event) => {
let stop: IDisposable;
if (this._fileService) {
stop = this._fileService.onFileChanges((event) => {
event.changes.forEach(change => {
const key = String(change.resource);
@@ -55,9 +57,10 @@ class ChangeRecorder {
array.push(change);
});
});
}
return {
stop: () => { stop.dispose(); },
stop: () => { return stop && stop.dispose(); },
hasChanged: (resource: URI) => !!changes[resource.toString()],
allChanges: () => flatten(values(changes))
};
@@ -273,14 +276,14 @@ export interface BulkEdit {
finish(): TPromise<ISelection>;
}
export function bulkEdit(fileService: IFileService, textModelResolverService: ITextModelResolverService, editor: ICommonCodeEditor, edits: IResourceEdit[], progress: IProgressRunner = null): TPromise<any> {
let bulk = createBulkEdit(fileService, textModelResolverService, editor);
export function bulkEdit(textModelResolverService: ITextModelResolverService, editor: ICommonCodeEditor, edits: IResourceEdit[], fileService?: IFileService, progress: IProgressRunner = null): TPromise<any> {
let bulk = createBulkEdit(textModelResolverService, editor, fileService);
bulk.add(edits);
bulk.progress(progress);
return bulk.finish();
}
export function createBulkEdit(fileService: IFileService, textModelResolverService: ITextModelResolverService, editor: ICommonCodeEditor): BulkEdit {
export function createBulkEdit(textModelResolverService: ITextModelResolverService, editor?: ICommonCodeEditor, fileService?: IFileService): BulkEdit {
let all: IResourceEdit[] = [];
let recording = new ChangeRecorder(fileService).start();
@@ -11,12 +11,11 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { ICommonCodeEditor, IEditorContribution, MouseTargetType, EditorContextKeys, IScrollEvent } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
@@ -125,9 +124,17 @@ export class ContextMenuController implements IEditorContribution {
private _getMenuActions(): IAction[] {
const result: IAction[] = [];
const contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService);
fillInActions(contextMenu, this._editor.getModel().uri, result);
let contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService);
const groups = contextMenu.getActions(this._editor.getModel().uri);
contextMenu.dispose();
for (let group of groups) {
const [, actions] = group;
result.push(...actions);
result.push(new Separator());
}
result.pop(); // remove last separator
return result;
}
@@ -105,10 +105,12 @@
}
.monaco-editor .find-widget.no-results .matchesCount {
background-color: rgba(255,0,0,0.5);
color: #A1260D;
}
.monaco-editor.vs-dark .find-widget.no-results .matchesCount {
background-color: rgba(255,0,0,0.3);
.monaco-editor.vs-dark .find-widget.no-results .matchesCount,
.monaco-editor.hc-black .find-widget.no-results .matchesCount {
color: #F48771
}
.monaco-editor .find-widget .matchesCount {
@@ -43,7 +43,7 @@ const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replac
const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode");
const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first 999 results are highlighted, but all find operations work on the entire text.");
const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}");
const NLS_NO_RESULTS = nls.localize('label.noResults', "No results");
const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results");
let MAX_MATCHES_COUNT_WIDTH = 69;
const WIDGET_FIXED_WIDTH = 411 - 69;
@@ -76,7 +76,7 @@ export class DefinitionAction extends EditorAction {
let result: Location[] = [];
for (let i = 0; i < references.length; i++) {
let reference = references[i];
if (!reference) {
if (!reference || !reference.range) {
continue;
}
let {uri, range} = reference;
@@ -110,7 +110,7 @@ export class DefinitionAction extends EditorAction {
} else {
let next = model.nearestReference(editor.getModel().uri, editor.getPosition());
this._openReference(editorService, next, this._configuration.openToSide).then(editor => {
if (model.references.length > 1) {
if (editor && model.references.length > 1) {
this._openInPeek(editorService, editor, model);
} else {
model.dispose();
@@ -128,7 +128,7 @@ export class DefinitionAction extends EditorAction {
revealIfVisible: !sideBySide
}
}, sideBySide).then(editor => {
return <editorCommon.IEditor>editor.getControl();
return editor && <editorCommon.IEditor>editor.getControl();
});
}
@@ -8,6 +8,8 @@
display: flex;
align-items: center;
justify-content: center;
height: 16px;
width: 16px;
}
.monaco-editor .lightbulb-glyph:hover {
@@ -16,12 +18,18 @@
.monaco-editor.vs .lightbulb-glyph {
background: url('lightbulb.svg') center center no-repeat;
height: 16px;
width: 16px;
}
.monaco-editor.vs-dark .lightbulb-glyph, .monaco-editor.hc-black .lightbulb-glyph {
background: url('lightbulb-dark.svg') center center no-repeat;
height: 16px;
width: 16px;
.monaco-editor.vs .lightbulb-glyph[data-severity="high"]{
background: url('lightbulb.svg') center center no-repeat;
}
.monaco-editor.vs-dark .lightbulb-glyph,
.monaco-editor.hc-black .lightbulb-glyph {
background: url('lightbulb-dark.svg') center center no-repeat;
}
.monaco-editor.vs-dark .lightbulb-glyph[data-severity="high"],
.monaco-editor.hc-black .lightbulb-glyph[data-severity="high"] {
background: url('lightbulb-dark.svg') center center no-repeat;
}
@@ -7,9 +7,10 @@
import 'vs/css!./lightBulbWidget';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { Emitter, any } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import * as dom from 'vs/base/browser/dom';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { QuickFixComputeEvent } from './quickFixModel';
import { QuickFixComputeEvent } from 'vs/editor/contrib/quickFix/common/quickFixModel';
export class LightBulbWidget implements IOverlayWidget, IDisposable {
@@ -86,7 +87,7 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable {
const modelNow = this._model;
e.fixes.done(fixes => {
if (modelNow === this._model && fixes && fixes.length > 0) {
this.show(e.range.startLineNumber);
this.show(e);
} else {
this.hide();
}
@@ -99,7 +100,8 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable {
return this._model;
}
show(line: number): void {
show(e: QuickFixComputeEvent): void {
const line = e.range.startLineNumber;
if (!this._hasSpaceInGlyphMargin(line)) {
return;
}
@@ -107,6 +109,7 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable {
this._line = line;
this._visible = true;
this._layout();
this._domNode.dataset['severity'] = e.severity >= Severity.Warning ? 'high' : '';
}
}
@@ -16,9 +16,9 @@ import { ICommonCodeEditor, EditorContextKeys, ModeContextKeys, IEditorContribut
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { QuickFixContextMenu } from './quickFixWidget';
import { LightBulbWidget } from './lightBulbWidget';
import { QuickFixModel, QuickFixComputeEvent } from './quickFixModel';
import { QuickFixContextMenu } from 'vs/editor/contrib/quickFix/browser/quickFixWidget';
import { LightBulbWidget } from 'vs/editor/contrib/quickFix/browser/lightBulbWidget';
import { QuickFixModel, QuickFixComputeEvent } from 'vs/editor/contrib/quickFix/common/quickFixModel';
@editorContribution
export class QuickFixController implements IEditorContribution {
@@ -88,7 +88,7 @@ export class QuickFixController implements IEditorContribution {
}
public triggerFromEditorSelection(): void {
this._model.triggerManual(this._editor.getSelection());
this._model.triggerManual();
}
private _updateLightBulbTitle(): void {
@@ -6,21 +6,21 @@
import * as arrays from 'vs/base/common/arrays';
import Event, { Emitter } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
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 { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ICommonCodeEditor, IPosition, IRange } from 'vs/editor/common/editorCommon';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CodeActionProviderRegistry, CodeAction } from 'vs/editor/common/modes';
import { getCodeActions } from '../common/quickFix';
class QuickFixOracle {
export class QuickFixOracle {
private _disposables: IDisposable[] = [];
private _currentRange: IRange;
constructor(private _editor: ICommonCodeEditor, private _markerService: IMarkerService, private _signalChange: (e: QuickFixComputeEvent) => any) {
@@ -34,29 +34,57 @@ class QuickFixOracle {
this._disposables = dispose(this._disposables);
}
private _onMarkerChanges(resources: URI[]): void {
const {uri} = this._editor.getModel();
let affectedBy = false;
for (const resource of resources) {
if (resource.toString() === uri.toString()) {
affectedBy = true;
break;
trigger(): void {
let {range, severity} = this._rangeAtPosition();
if (!range) {
range = this._editor.getSelection();
}
}
if (affectedBy) {
this._onCursorChange();
}
}
private _onCursorChange(): void {
const range = this._markerAtPosition() || this._wordAtPosition();
this._signalChange({
type: 'auto',
type: 'manual',
severity,
range,
position: this._editor.getPosition(),
fixes: range && getCodeActions(this._editor.getModel(), this._editor.getModel().validateRange(range))
});
}
private _onMarkerChanges(resources: URI[]): void {
const {uri} = this._editor.getModel();
for (const resource of resources) {
if (resource.toString() === uri.toString()) {
this._onCursorChange();
return;
}
}
}
private _onCursorChange(): void {
const {range, severity} = this._rangeAtPosition();
if (!Range.equalsRange(this._currentRange, range)) {
this._currentRange = range;
this._signalChange({
type: 'auto',
severity,
range,
position: this._editor.getPosition(),
fixes: range && getCodeActions(this._editor.getModel(), this._editor.getModel().validateRange(range))
});
}
}
private _rangeAtPosition(): { range: IRange, severity: Severity; } {
let range: IRange;
let severity: Severity;
const marker = this._markerAtPosition();
if (marker) {
range = Range.lift(marker);
severity = marker.severity;
} else {
range = this._wordAtPosition();
severity = Severity.Info;
}
return { range, severity };
}
private _markerAtPosition(): IMarker {
@@ -76,26 +104,23 @@ class QuickFixOracle {
}
private _wordAtPosition(): IRange {
return;
// todo@joh - enable once we decide to eagerly show the
// light bulb as the cursor moves
// const {positionLineNumber, positionColumn} = this._editor.getSelection();
// const model = this._editor.getModel();
// const info = model.getWordAtPosition({ lineNumber: positionLineNumber, column: positionColumn });
// if (info) {
// return {
// startLineNumber: positionLineNumber,
// startColumn: info.startColumn,
// endLineNumber: positionLineNumber,
// endColumn: info.endColumn
// };
// }
const {positionLineNumber, positionColumn} = this._editor.getSelection();
const model = this._editor.getModel();
const info = model.getWordAtPosition({ lineNumber: positionLineNumber, column: positionColumn });
if (info) {
return {
startLineNumber: positionLineNumber,
startColumn: info.startColumn,
endLineNumber: positionLineNumber,
endColumn: info.endColumn
};
}
}
}
export interface QuickFixComputeEvent {
type: 'auto' | 'manual';
severity: Severity;
range: IRange;
position: IPosition;
fixes: TPromise<CodeAction[]>;
@@ -109,7 +134,7 @@ export class QuickFixModel {
private _onDidChangeFixes = new Emitter<QuickFixComputeEvent>();
private _disposables: IDisposable[] = [];
constructor(editor: ICodeEditor, markerService: IMarkerService) {
constructor(editor: ICommonCodeEditor, markerService: IMarkerService) {
this._editor = editor;
this._markerService = markerService;
@@ -132,7 +157,8 @@ export class QuickFixModel {
private _update(): void {
if (this._quickFixOracle) {
dispose(this._quickFixOracle);
this._quickFixOracle.dispose();
this._quickFixOracle = undefined;
this._onDidChangeFixes.fire(undefined);
}
@@ -144,16 +170,9 @@ export class QuickFixModel {
}
}
triggerManual(selection: Selection): void {
const model = this._editor.getModel();
if (model) {
const fixes = getCodeActions(model, selection);
this._onDidChangeFixes.fire({
type: 'manual',
range: selection,
position: { lineNumber: selection.positionLineNumber, column: selection.positionColumn },
fixes
});
triggerManual(): void {
if (this._quickFixOracle) {
this._quickFixOracle.trigger();
}
}
}
@@ -0,0 +1,129 @@
/*---------------------------------------------------------------------------------------------
* 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 { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { Model } from 'vs/editor/common/model/model';
import { mockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { MarkerService } from 'vs/platform/markers/common/markerService';
import { QuickFixOracle } from 'vs/editor/contrib/quickFix/common/quickFixModel';
import { CodeActionProviderRegistry } from 'vs/editor/common/modes';
suite('QuickFix', () => {
let uri = URI.parse('fake:path');
let model = Model.createFromString('foobar foo bar\nfarboo far boo', undefined, 'foo-lang', uri);
let markerService: MarkerService;
let editor: ICommonCodeEditor;
let reg = CodeActionProviderRegistry.register('foo-lang', {
provideCodeActions() {
return [{ command: { id: 'test-command', title: 'test', arguments: [] }, score: 1 }];
}
});
setup(() => {
markerService = new MarkerService();
editor = mockCodeEditor([], { model });
editor.setPosition({ lineNumber: 1, column: 1 });
});
suiteTeardown(() => {
reg.dispose();
model.dispose();
});
test('Orcale -> marker added', done => {
const oracle = new QuickFixOracle(editor, markerService, e => {
assert.equal(e.type, 'auto');
assert.ok(e.fixes);
e.fixes.then(fixes => {
oracle.dispose();
assert.equal(fixes.length, 1);
done();
}, done);
});
// start here
markerService.changeOne('fake', uri, [{
startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,
message: 'error',
severity: 1,
code: '',
source: ''
}]);
});
test('Orcale -> position changed', done => {
markerService.changeOne('fake', uri, [{
startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,
message: 'error',
severity: 1,
code: '',
source: ''
}]);
editor.setPosition({ lineNumber: 2, column: 1 });
const oracle = new QuickFixOracle(editor, markerService, e => {
assert.equal(e.type, 'auto');
assert.ok(e.fixes);
e.fixes.then(fixes => {
oracle.dispose();
assert.equal(fixes.length, 1);
done();
}, done);
});
// start here
editor.setPosition({ lineNumber: 1, column: 1 });
});
test('Oracle -> ask once per marker/word', () => {
let counter = 0;
let reg = CodeActionProviderRegistry.register('foo-lang', {
provideCodeActions() {
counter += 1;
return [];
}
});
markerService.changeOne('fake', uri, [{
startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,
message: 'error',
severity: 1,
code: '',
source: ''
}]);
let fixes: TPromise<any>[] = [];
let oracle = new QuickFixOracle(editor, markerService, e => {
fixes.push(e.fixes);
});
editor.setPosition({ lineNumber: 1, column: 3 }); // marker
editor.setPosition({ lineNumber: 1, column: 6 }); // (same) marker
editor.setPosition({ lineNumber: 1, column: 8 }); // whitespace
editor.setPosition({ lineNumber: 2, column: 2 }); // word
editor.setPosition({ lineNumber: 2, column: 6 }); // (same) word
return TPromise.join(fixes).then(_ => {
reg.dispose();
oracle.dispose();
assert.equal(counter, 2);
});
});
});
@@ -22,6 +22,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { rename } from '../common/rename';
import RenameInputField from './renameInputField';
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
import { optional } from 'vs/platform/instantiation/common/instantiation';
// --- register actions and commands
@@ -42,10 +43,10 @@ class RenameController implements IEditorContribution {
constructor(
private editor: ICodeEditor,
@IMessageService private _messageService: IMessageService,
@IFileService private _fileService: IFileService,
@ITextModelResolverService private _textModelResolverService: ITextModelResolverService,
@IProgressService private _progressService: IProgressService,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@optional(IFileService) private _fileService: IFileService
) {
this._renameInputField = new RenameInputField(editor);
this._renameInputVisible = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService);
@@ -132,7 +133,7 @@ class RenameController implements IEditorContribution {
// start recording of file changes so that we can figure out if a file that
// is to be renamed conflicts with another (concurrent) modification
let edit = createBulkEdit(this._fileService, this._textModelResolverService, <ICodeEditor>this.editor);
let edit = createBulkEdit(this._textModelResolverService, <ICodeEditor>this.editor, this._fileService);
return rename(this.editor.getModel(), this.editor.getPosition(), newName).then(result => {
if (result.rejectReason) {
@@ -122,15 +122,15 @@ export class CodeSnippet implements ICodeSnippet {
for (let {startLineNumber, startColumn, endLineNumber, endColumn} of originalPlaceHolder.occurences) {
if (startColumn > 1) {
// placeholders that aren't at the beginning of the snippet line
if (startColumn > 1 || startLineNumber === 1) {
// placeholders that aren't at the beginning of new snippet lines
// will be moved by how many characters the indentation has been
// adjusted
startColumn = startColumn + deltaColumns[startLineNumber];
endColumn = endColumn + deltaColumns[endLineNumber];
} else {
// placeholders at the beginning of the snippet line
// placeholders at the beginning of new snippet lines
// will be indented by the reference indentation
startColumn += referenceIndentation.length;
endColumn += referenceIndentation.length;
@@ -140,7 +140,7 @@ export class CodeSnippet implements ICodeSnippet {
startLineNumber: startLineNumber + deltaLine,
startColumn,
endLineNumber: endLineNumber + deltaLine,
endColumn
endColumn,
});
}
@@ -225,7 +225,7 @@ suite('Editor Contrib - Snippets', () => {
});
test('issue #11890: Bad cursor position', () => {
test('issue #11890: Bad cursor position 1/2', () => {
let snippet = CodeSnippet.fromTextmate([
'afterEach((done) => {',
@@ -242,6 +242,7 @@ suite('Editor Contrib - Snippets', () => {
assert.equal(boundSnippet.lines[1], ' test');
assert.equal(boundSnippet.placeHolders.length, 3);
assert.equal(boundSnippet.finishPlaceHolderIndex, 2);
let [first, second] = boundSnippet.placeHolders;
assert.equal(first.occurences.length, 1);
assert.equal(first.occurences[0].startColumn, 1);
@@ -249,6 +250,49 @@ suite('Editor Contrib - Snippets', () => {
assert.equal(second.occurences[0].startColumn, 7);
});
test('issue #11890: Bad cursor position 2/2', () => {
let snippet = CodeSnippet.fromTextmate('${1}\ttest');
let boundSnippet = snippet.bind('abc abc abc prefix3', 0, 12, {
normalizeIndentation(str: string): string {
return str.replace(/\t/g, ' ');
}
});
assert.equal(boundSnippet.lines[0], '\ttest');
assert.equal(boundSnippet.placeHolders.length, 2);
assert.equal(boundSnippet.finishPlaceHolderIndex, 1);
let [first, second] = boundSnippet.placeHolders;
assert.equal(first.occurences.length, 1);
assert.equal(first.occurences[0].startColumn, 13);
assert.equal(second.occurences.length, 1);
assert.equal(second.occurences[0].startColumn, 18);
});
test('issue #17989: Bad selection', () => {
let snippet = CodeSnippet.fromTextmate('${1:HoldMeTight}');
let boundSnippet = snippet.bind('abc abc abc prefix3', 0, 12, {
normalizeIndentation(str: string): string {
return str.replace(/\t/g, ' ');
}
});
assert.equal(boundSnippet.lines[0], 'HoldMeTight');
assert.equal(boundSnippet.placeHolders.length, 2);
assert.equal(boundSnippet.finishPlaceHolderIndex, 1);
let [first, second] = boundSnippet.placeHolders;
assert.equal(first.occurences.length, 1);
assert.equal(first.occurences[0].startColumn, 13);
assert.equal(second.occurences.length, 1);
assert.equal(second.occurences[0].startColumn, 24);
});
test('variables, simple', () => {
const resolver: ISnippetVariableResolver = {
@@ -9,9 +9,8 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { forEach } from 'vs/base/common/collections';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { startsWith } from 'vs/base/common/strings';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICommonCodeEditor, ICursorSelectionChangedEvent, CursorChangeReason, IModel, IPosition } from 'vs/editor/common/editorCommon';
import { ICommonCodeEditor, ICursorSelectionChangedEvent, CursorChangeReason, IModel, IPosition, IWordAtPosition } from 'vs/editor/common/editorCommon';
import { ISuggestSupport, SuggestRegistry } from 'vs/editor/common/modes';
import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest';
import { CompletionModel } from './completionModel';
@@ -30,104 +29,51 @@ export interface ISuggestEvent {
auto: boolean;
}
export class Context {
export class LineContext {
static shouldAutoTrigger(editor: ICommonCodeEditor): boolean {
const model = editor.getModel();
if (!model) {
return false;
}
const pos = editor.getPosition();
const word = model.getWordAtPosition(pos);
if (!word) {
return false;
}
if (word.endColumn !== pos.column) {
return false;
}
if (!isNaN(Number(word.word))) {
return false;
}
return true;
}
static isInEditableRange(editor: ICommonCodeEditor): boolean {
const model = editor.getModel();
const position = editor.getPosition();
if (model.hasEditableRange()) {
const editableRange = model.getEditableRange();
if (!editableRange.containsPosition(position)) {
return false;
}
}
return true;
}
readonly lineNumber: number;
readonly column: number;
readonly isInEditableRange: boolean;
readonly lineContentBefore: string;
readonly wordBefore: string;
readonly wordAfter: string;
constructor(model: IModel, position: IPosition, private auto: boolean) {
const lineContent = model.getLineContent(position.lineNumber);
const wordUnderCursor = model.getWordAtPosition(position);
if (wordUnderCursor) {
this.wordBefore = lineContent.substring(wordUnderCursor.startColumn - 1, position.column - 1);
this.wordAfter = lineContent.substring(position.column - 1, wordUnderCursor.endColumn - 1);
} else {
this.wordBefore = '';
this.wordAfter = '';
}
readonly leadingLineContent: string;
readonly leadingWord: IWordAtPosition;
readonly auto;
constructor(model: IModel, position: IPosition, auto: boolean) {
this.leadingLineContent = model.getLineContent(position.lineNumber).substr(0, position.column - 1);
this.leadingWord = model.getWordUntilPosition(position);
this.lineNumber = position.lineNumber;
this.column = position.column;
this.lineContentBefore = lineContent.substr(0, position.column - 1);
this.isInEditableRange = true;
if (model.hasEditableRange()) {
const editableRange = model.getEditableRange();
if (!editableRange.containsPosition(position)) {
this.isInEditableRange = false;
}
}
}
shouldAutoTrigger(): boolean {
if (this.wordBefore.length === 0) {
// Word before position is empty
return false;
}
if (!isNaN(Number(this.wordBefore))) {
// Word before is number only
return false;
}
if (this.wordAfter.length > 0) {
// Word after position is non empty
return false;
}
return true;
}
isDifferentContext(context: Context): boolean {
if (this.lineNumber !== context.lineNumber) {
// Line number has changed
return true;
}
if (context.column < this.column - this.wordBefore.length) {
// column went before word start
return true;
}
if (!startsWith(context.lineContentBefore, this.lineContentBefore)) {
// Line has changed before position
return true;
}
if (context.wordBefore === '' && context.lineContentBefore !== this.lineContentBefore) {
// Most likely a space has been typed
return true;
}
return false;
}
shouldRetrigger(context: Context): boolean {
if (!startsWith(this.lineContentBefore, context.lineContentBefore)) {
// Doesn't look like the same line
return false;
}
if (this.lineContentBefore.length > context.lineContentBefore.length && this.wordBefore.length === 0) {
// Text was deleted and previous current word was empty
return false;
}
if (this.auto && context.wordBefore.length === 0) {
// Currently in auto mode and new current word is empty
return false;
}
return true;
this.auto = auto;
}
}
@@ -147,7 +93,7 @@ export class SuggestModel implements IDisposable {
private state: State;
private requestPromise: TPromise<void>;
private context: Context;
private context: LineContext;
private completionModel: CompletionModel;
@@ -274,12 +220,11 @@ export class SuggestModel implements IDisposable {
}
private onCursorChange(e: ICursorSelectionChangedEvent): void {
if (!e.selection.isEmpty()) {
this.cancel();
return;
}
if (e.source !== 'keyboard' || e.reason !== CursorChangeReason.NotSet) {
if (!e.selection.isEmpty()
|| e.source !== 'keyboard'
|| e.reason !== CursorChangeReason.NotSet) {
this.cancel();
return;
}
@@ -288,32 +233,28 @@ export class SuggestModel implements IDisposable {
return;
}
const isInactive = this.state === State.Idle;
if (isInactive && !this.editor.getConfiguration().contribInfo.quickSuggestions) {
return;
}
const model = this.editor.getModel();
if (!model) {
return;
}
const ctx = new Context(model, this.editor.getPosition(), false);
if (this.state === State.Idle) {
if (isInactive) {
// trigger was not called or it was canceled
if (this.editor.getConfiguration().contribInfo.quickSuggestions) {
// trigger 24x7 IntelliSense when idle and enabled
this.cancel();
if (ctx.shouldAutoTrigger()) {
if (LineContext.shouldAutoTrigger(this.editor)) {
this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay);
this.triggerAutoSuggestPromise.then(() => {
this.triggerAutoSuggestPromise = null;
this.trigger(true);
});
}
}
} else {
// refine active suggestion
const ctx = new LineContext(model, this.editor.getPosition(), this.state === State.Auto);
this.onNewContext(ctx);
}
}
@@ -326,9 +267,9 @@ export class SuggestModel implements IDisposable {
return;
}
const ctx = new Context(model, this.editor.getPosition(), auto);
const ctx = new LineContext(model, this.editor.getPosition(), auto);
if (!ctx.isInEditableRange) {
if (!LineContext.isInEditableRange(this.editor)) {
return;
}
@@ -359,9 +300,9 @@ export class SuggestModel implements IDisposable {
items = items.concat(existingItems).sort(cmpFn);
}
const ctx = new Context(model, this.editor.getPosition(), auto);
const ctx = new LineContext(model, this.editor.getPosition(), auto);
this.completionModel = new CompletionModel(items, this.context.column, {
leadingLineContent: ctx.lineContentBefore,
leadingLineContent: ctx.leadingLineContent,
characterCountDelta: this.context ? ctx.column - this.context.column : 0
});
this.onNewContext(ctx);
@@ -369,42 +310,66 @@ export class SuggestModel implements IDisposable {
}).then(null, onUnexpectedError);
}
private onNewContext(ctx: Context): void {
if (this.context && this.context.isDifferentContext(ctx)) {
if (this.context.shouldRetrigger(ctx)) {
this.trigger(this.state === State.Auto, true);
} else {
this.cancel();
}
private onNewContext(ctx: LineContext): void {
} else if (this.completionModel) {
if (this.completionModel.incomplete && ctx.column > this.context.column) {
const {complete, incomplete} = this.completionModel.resolveIncompleteInfo();
this.trigger(this.state === State.Auto, true, incomplete, complete);
if (!this.context) {
// happens when 24x7 IntelliSense is enabled and still in its delay
return;
}
const auto = this.state === State.Auto;
const oldLineContext = this.completionModel.lineContext;
if (ctx.lineNumber !== this.context.lineNumber) {
// e.g. happens when pressing Enter while IntelliSense is computed
this.cancel();
return;
}
if (ctx.column < this.context.column) {
// typed -> moved cursor LEFT -> retrigger if still on a word
if (ctx.leadingWord.word) {
this.trigger(this.context.auto, true);
}
return;
}
if (!this.completionModel) {
// happens when IntelliSense is not yet computed
return;
}
if (ctx.column > this.context.column && this.completionModel.incomplete) {
// typed -> moved cursor RIGHT & incomple model -> retrigger
const {complete, incomplete} = this.completionModel.resolveIncompleteInfo();
this.trigger(this.state === State.Auto, true, incomplete, complete);
} else {
// typed -> moved cursor RIGHT -> update UI
let oldLineContext = this.completionModel.lineContext;
let isFrozen = false;
this.completionModel.lineContext = {
leadingLineContent: ctx.lineContentBefore,
characterCountDelta: this.context ? ctx.column - this.context.column : 0
leadingLineContent: ctx.leadingLineContent,
characterCountDelta: ctx.column - this.context.column
};
// when explicitly request when the next context goes
// from 'results' to 'no results' freeze
if (!auto && this.completionModel.items.length === 0) {
if (this.completionModel.items.length === 0) {
if (LineContext.shouldAutoTrigger(this.editor) && this.context.leadingWord.endColumn < ctx.leadingWord.startColumn) {
// retrigger when heading into a new word
this.trigger(this.context.auto, true);
return;
}
if (!this.context.auto) {
// freeze when IntelliSense was manually requested
this.completionModel.lineContext = oldLineContext;
isFrozen = this.completionModel.items.length > 0;
}
}
this._onDidSuggest.fire({
completionModel: this.completionModel,
auto: this.context.auto,
isFrozen,
auto
});
}
}
@@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { Model } from 'vs/editor/common/model/model';
import { ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon';
import { ISuggestSupport, ISuggestResult, SuggestRegistry } from 'vs/editor/common/modes';
import { SuggestModel, Context } from 'vs/editor/contrib/suggest/common/suggestModel';
import { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/common/suggestModel';
import { MockCodeEditor, MockScopeLocation } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
@@ -49,8 +49,10 @@ suite('SuggestModel - Context', function () {
function assertAutoTrigger(offset: number, expected: boolean): void {
const pos = model.getPositionAt(offset);
const ctx = new Context(model, pos, false);
assert.equal(ctx.shouldAutoTrigger(), expected);
const editor = createMockEditor(model);
editor.setPosition(pos);
assert.equal(LineContext.shouldAutoTrigger(editor), expected);
editor.dispose();
}
assertAutoTrigger(3, true); // end of word, Das|
@@ -59,28 +61,6 @@ suite('SuggestModel - Context', function () {
assertAutoTrigger(55, false); // number, 1861|
});
test('Context - isDifferentContext', function () {
// different line
const ctx = new Context(model, { lineNumber: 1, column: 8 }, true); // Das Pfer|d
assert.equal(ctx.isDifferentContext(new Context(model, { lineNumber: 2, column: 1 }, true)), true);
function createEndContext(value: string) {
const model = Model.createFromString(value);
const ctx = new Context(model, model.getPositionAt(value.length), true); // Das Pfer|d
return ctx;
}
// got shorter -> redo
assert.equal(createEndContext('One Two').isDifferentContext(createEndContext('One Tw')), true);
// got longer inside word -> keep
assert.equal(createEndContext('One Tw').isDifferentContext(createEndContext('One Two')), false);
// got longer new word -> redo
assert.equal(createEndContext('One Two').isDifferentContext(createEndContext('One Two ')), true);
});
});
suite('SuggestModel - TriggerAndCancelOracle', function () {
@@ -239,4 +219,49 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
});
});
});
test('#17400: Keep filtering suggestModel.ts after space', function () {
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
triggerCharacters: [],
provideCompletionItems(doc, pos) {
return <ISuggestResult>{
currentWord: '',
incomplete: false,
suggestions: [{
label: 'My Table',
type: 'property',
insertText: 'My Table'
}]
};
}
}));
model.setValue('');
return withOracle((model, editor) => {
return assertEvent(model.onDidSuggest, () => {
editor.setPosition({ lineNumber: 1, column: 1 });
editor.trigger('keyboard', Handler.Type, { text: 'My' });
}, event => {
assert.equal(event.auto, true);
assert.equal(event.completionModel.items.length, 1);
const [first] = event.completionModel.items;
assert.equal(first.suggestion.label, 'My Table');
return assertEvent(model.onDidSuggest, () => {
editor.setPosition({ lineNumber: 1, column: 3 });
editor.trigger('keyboard', Handler.Type, { text: ' ' });
}, event => {
assert.equal(event.auto, true);
assert.equal(event.completionModel.items.length, 1);
const [first] = event.completionModel.items;
assert.equal(first.suggestion.label, 'My Table');
});
});
});
});
});
@@ -17,7 +17,7 @@ import { domEvent } from 'vs/base/browser/event';
import { Emitter } from 'vs/base/common/event';
export function fillInActions(menu: IMenu, context: any, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, ignoreAltKey?: boolean): void {
export function fillInActions(menu: IMenu, context: any, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }): void {
const groups = menu.getActions(context);
if (groups.length === 0) {
return;
@@ -25,11 +25,6 @@ export function fillInActions(menu: IMenu, context: any, target: IAction[] | { p
for (let tuple of groups) {
let [group, actions] = tuple;
if (!ignoreAltKey && _altKey.value) {
swapWithAltActionsIfPossible(actions);
}
if (group === 'navigation') {
const head = Array.isArray<IAction>(target) ? target : target.primary;
@@ -67,13 +62,6 @@ export function fillInActions(menu: IMenu, context: any, target: IAction[] | { p
}
}
function swapWithAltActionsIfPossible(actions: MenuItemAction[]): void {
for (let i = 0; i < actions.length; i++) {
if (actions[i].alt) {
actions[i] = actions[i].alt;
}
}
}
export function createActionItem(action: IAction, keybindingService: IKeybindingService, messageService: IMessageService): ActionItem {
if (action instanceof MenuItemAction) {
@@ -86,8 +74,6 @@ const _altKey = new class extends Emitter<boolean> {
private _subscriptions: IDisposable[] = [];
private _value = false;
constructor() {
super();
@@ -97,15 +83,6 @@ const _altKey = new class extends Emitter<boolean> {
this._subscriptions.push(domEvent(document.body, 'blur')(e => this.fire(false)));
}
fire(value: boolean) {
super.fire(value);
this._value = value;
}
get value() {
return this._value;
}
dispose() {
super.dispose();
this._subscriptions = dispose(this._subscriptions);
@@ -82,7 +82,7 @@ export function getConfigurationValue<T>(config: any, settingPath: string, defau
let current = config;
for (let i = 0; i < path.length; i++) {
current = current[path[i]];
if (typeof current === 'undefined') {
if (typeof current === 'undefined' || current === null) {
return undefined;
}
}
+6
View File
@@ -9,6 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import Event from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
export const IEditorService = createDecorator<IEditorService>('editorService');
@@ -238,6 +239,11 @@ export interface ITextEditorOptions extends IEditorOptions {
endColumn?: number;
};
/**
* Text editor view state.
*/
viewState?: IEditorViewState;
/**
* Option to scroll vertically or horizontally as necessary and reveal a range centered vertically only if it lies outside the viewport.
*/
@@ -14,6 +14,7 @@ export interface ParsedArgs {
diff?: boolean;
goto?: boolean;
'new-window'?: boolean;
'new-window-if-not-first'?: boolean;
'reuse-window'?: boolean;
locale?: string;
'user-data-dir'?: string;
+2 -1
View File
@@ -30,6 +30,7 @@ const options: minimist.Opts = {
'diff',
'goto',
'new-window',
'new-window-if-not-first',
'reuse-window',
'performance',
'verbose',
@@ -148,7 +149,7 @@ export function formatOptions(options: { [name: string]: string; }, columns: num
}
function wrapText(text: string, columns: number): string[] {
let lines = [];
let lines: string[] = [];
while (text.length) {
let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns);
let line = text.slice(0, index).trim();
+3 -3
View File
@@ -223,7 +223,7 @@ export class FileChangesEvent extends events.Event {
return false;
}
return this._changes.some((change) => {
return this._changes.some(change => {
if (change.type !== type) {
return false;
}
@@ -280,11 +280,11 @@ export class FileChangesEvent extends events.Event {
}
private getOfType(type: FileChangeType): IFileChange[] {
return this._changes.filter((change) => change.type === type);
return this._changes.filter(change => change.type === type);
}
private hasType(type: FileChangeType): boolean {
return this._changes.some((change) => {
return this._changes.some(change => {
return change.type === type;
});
}
@@ -123,13 +123,15 @@ export class InstantiationService implements IInstantiationService {
// arguments defined by service decorators
let serviceDependencies = _util.getServiceDependencies(desc.ctor).sort((a, b) => a.index - b.index);
let serviceArgs = serviceDependencies.map(dependency => {
let serviceArgs: any[] = [];
for (const dependency of serviceDependencies) {
let service = this._getOrCreateServiceInstance(dependency.id);
if (!service && this._strict && !dependency.optional) {
throw new Error(`[createInstance] ${desc.ctor.name} depends on UNKNOWN service ${dependency.id}.`);
}
return service;
});
serviceArgs.push(service);
}
let firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : staticArgs.length;
// check for argument mismatches, adjust static args if needed
@@ -23,7 +23,7 @@ export interface ICommandAndKeybindingRule extends IKeybindingRule {
}
export interface IKeybindingsRegistry {
registerKeybindingRule(rule: IKeybindingRule);
registerKeybindingRule(rule: IKeybindingRule): void;
registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): void;
getDefaultKeybindings(): IKeybindingItem[];
+4 -3
View File
@@ -25,6 +25,7 @@ export interface IWindowsService {
openDevTools(windowId: number): TPromise<void>;
toggleDevTools(windowId: number): TPromise<void>;
// TODO@joao: rename, shouldn't this be closeWindow?
// @ben: no, this actually leaves the window open but changes it to have no workspace opened
closeFolder(windowId: number): TPromise<void>;
toggleFullScreen(windowId: number): TPromise<void>;
setRepresentedFilename(windowId: number, fileName: string): TPromise<void>;
@@ -40,8 +41,7 @@ export interface IWindowsService {
quit(): TPromise<void>;
// Global methods
// TODO@joao: rename, shouldn't this be openWindow?
windowOpen(paths: string[], forceNewWindow?: boolean): TPromise<void>;
openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void>;
openNewWindow(): TPromise<void>;
showWindow(windowId: number): TPromise<void>;
getWindows(): TPromise<{ id: number; path: string; title: string; }[]>;
@@ -87,7 +87,8 @@ export interface IWindowService {
}
export interface IWindowSettings {
openFilesInNewWindow: boolean;
openFilesInNewWindow: 'on' | 'off' | 'default';
openFoldersInNewWindow: 'on' | 'off' | 'default';
reopenFolders: 'all' | 'one' | 'none';
restoreFullscreen: boolean;
zoomLevel: number;
+4 -4
View File
@@ -31,7 +31,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise<void>;
call(command: 'toggleMenuBar', arg: number): TPromise<void>;
call(command: 'quit'): TPromise<void>;
call(command: 'windowOpen', arg: [string[], boolean]): TPromise<void>;
call(command: 'openWindow', arg: [string[], { forceNewWindow?: boolean, forceReuseWindow?: boolean }]): TPromise<void>;
call(command: 'openNewWindow'): TPromise<void>;
call(command: 'showWindow', arg: number): TPromise<void>;
call(command: 'getWindows'): TPromise<{ id: number; path: string; title: string; }[]>;
@@ -76,7 +76,7 @@ export class WindowsChannel implements IWindowsChannel {
case 'unmaximizeWindow': return this.service.unmaximizeWindow(arg);
case 'setDocumentEdited': return this.service.setDocumentEdited(arg[0], arg[1]);
case 'toggleMenuBar': return this.service.toggleMenuBar(arg);
case 'windowOpen': return this.service.windowOpen(arg[0], arg[1]);
case 'openWindow': return this.service.openWindow(arg[0], arg[1]);
case 'openNewWindow': return this.service.openNewWindow();
case 'showWindow': return this.service.showWindow(arg);
case 'getWindows': return this.service.getWindows();
@@ -179,8 +179,8 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('quit');
}
windowOpen(paths: string[], forceNewWindow?: boolean): TPromise<void> {
return this.channel.call('windowOpen', [paths, forceNewWindow]);
openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void> {
return this.channel.call('openWindow', [paths, options]);
}
openNewWindow(): TPromise<void> {
@@ -16,7 +16,7 @@ import { fromEventEmitter } from 'vs/base/node/event';
import { IURLService } from 'vs/platform/url/common/url';
// TODO@Joao: remove this dependency, move all implementation to this class
import { IWindowsMainService } from 'vs/code/electron-main/windows';
import { IWindowsMainService, OpenContext } from 'vs/code/electron-main/windows';
export class WindowsService implements IWindowsService, IDisposable {
@@ -94,7 +94,7 @@ export class WindowsService implements IWindowsService, IDisposable {
const vscodeWindow = this.windowsMainService.getWindowById(windowId);
if (vscodeWindow) {
this.windowsMainService.open({ cli: this.environmentService.args, forceEmpty: true, windowToUse: vscodeWindow });
this.windowsMainService.open({ context: OpenContext.OTHER, cli: this.environmentService.args, forceEmpty: true, windowToUse: vscodeWindow });
}
return TPromise.as(null);
@@ -198,17 +198,17 @@ export class WindowsService implements IWindowsService, IDisposable {
return TPromise.as(null);
}
windowOpen(paths: string[], forceNewWindow?: boolean): TPromise<void> {
openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void> {
if (!paths || !paths.length) {
return TPromise.as(null);
}
this.windowsMainService.open({ cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: forceNewWindow });
this.windowsMainService.open({ context: OpenContext.OTHER, cli: this.environmentService.args, pathsToOpen: paths, forceNewWindow: options && options.forceNewWindow, forceReuseWindow: options && options.forceReuseWindow });
return TPromise.as(null);
}
openNewWindow(): TPromise<void> {
this.windowsMainService.openNewWindow();
this.windowsMainService.openNewWindow(OpenContext.OTHER);
return TPromise.as(null);
}
@@ -271,7 +271,7 @@ export class WindowsService implements IWindowsService, IDisposable {
const cli = assign(Object.create(null), this.environmentService.args, { goto: true });
const pathsToOpen = [filePath];
this.windowsMainService.open({ cli, pathsToOpen });
this.windowsMainService.open({ context: OpenContext.OTHER, cli, pathsToOpen });
return TPromise.as(null);
}
@@ -579,7 +579,7 @@ export class MainThreadEditorsTracker {
}
private _findVisibleTextEditorIds(): string[] {
let result = [];
let result: string[] = [];
let modelUris = Object.keys(this._model2TextEditors);
for (let i = 0, len = modelUris.length; i < len; i++) {
let editors = this._model2TextEditors[modelUris[i]];
@@ -16,7 +16,7 @@ export class MainThreadTreeExplorers extends MainThreadTreeExplorersShape {
private _proxy: ExtHostTreeExplorersShape;
constructor(
@IThreadService private threadService: IThreadService,
@IThreadService threadService: IThreadService,
@ITreeExplorerService private treeExplorerService: ITreeExplorerService,
@IMessageService private messageService: IMessageService,
@ICommandService private commandService: ICommandService
@@ -30,9 +30,9 @@ export class MainThreadWorkspace extends MainThreadWorkspaceShape {
constructor(
@ISearchService searchService: ISearchService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@ITextFileService textFileService,
@IWorkbenchEditorService editorService,
@ITextModelResolverService textModelResolverService,
@ITextFileService textFileService: ITextFileService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@ITextModelResolverService textModelResolverService: ITextModelResolverService,
@IFileService fileService: IFileService
) {
super();
@@ -99,7 +99,7 @@ export class MainThreadWorkspace extends MainThreadWorkspaceShape {
}
}
return bulkEdit(this._fileService, this._textModelResolverService, codeEditor, edits)
return bulkEdit(this._textModelResolverService, codeEditor, edits, this._fileService)
.then(() => true);
}
}
@@ -92,7 +92,7 @@ export class ContributableActionProvider implements IActionProvider {
private registry: IActionBarRegistry;
constructor() {
this.registry = (<IActionBarRegistry>Registry.as(Extensions.Actionbar));
this.registry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
}
private toContext(tree: ITree, element: any): any {
@@ -89,5 +89,5 @@ const schema: IJSONSchema =
}
};
const jsonRegistry = <IJSONContributionRegistry>Registry.as(JSONExtensions.JSONContribution);
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(schemaId, schema);

Some files were not shown because too many files have changed in this diff Show More