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 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. 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"> <p align="center">
<img alt="VS Code in action" src="https://cloud.githubusercontent.com/assets/11839736/16642200/6624dde0-43bd-11e6-8595-c81885ba0dc2.png"> <img alt="VS Code in action" src="https://cloud.githubusercontent.com/assets/11839736/16642200/6624dde0-43bd-11e6-8595-c81885ba0dc2.png">
</p> </p>
+3 -3
View File
@@ -39,8 +39,8 @@ const nodeModules = ['electron', 'original-fs']
// Build // Build
const builtInExtensions = [ const builtInExtensions = [
{ name: 'ms-vscode.node-debug', version: '1.9.0' }, { name: 'ms-vscode.node-debug', version: '1.9.1' },
{ name: 'ms-vscode.node-debug2', version: '1.9.0' } { name: 'ms-vscode.node-debug2', version: '1.9.1' }
]; ];
const vscodeEntryPoints = _.flatten([ const vscodeEntryPoints = _.flatten([
@@ -110,7 +110,7 @@ const config = {
version: packageJson.electronVersion, version: packageJson.electronVersion,
productAppName: product.nameLong, productAppName: product.nameLong,
companyName: 'Microsoft Corporation', companyName: 'Microsoft Corporation',
copyright: 'Copyright (C) 2016 Microsoft. All rights reserved', copyright: 'Copyright (C) 2017 Microsoft. All rights reserved',
darwinIcon: 'resources/darwin/code.icns', darwinIcon: 'resources/darwin/code.icns',
darwinBundleIdentifier: product.darwinBundleIdentifier, darwinBundleIdentifier: product.darwinBundleIdentifier,
darwinApplicationCategoryType: 'public.app-category.developer-tools', darwinApplicationCategoryType: 'public.app-category.developer-tools',
+15 -15
View File
@@ -188,21 +188,21 @@ function format(text) {
} }
function getDefaultOptions() { function getDefaultOptions() {
return { return {
IndentSize: 4, indentSize: 4,
TabSize: 4, tabSize: 4,
NewLineCharacter: '\r\n', newLineCharacter: '\r\n',
ConvertTabsToSpaces: true, convertTabsToSpaces: true,
IndentStyle: ts.IndentStyle.Block, indentStyle: ts.IndentStyle.Block,
InsertSpaceAfterCommaDelimiter: true, insertSpaceAfterCommaDelimiter: true,
InsertSpaceAfterSemicolonInForStatements: true, insertSpaceAfterSemicolonInForStatements: true,
InsertSpaceBeforeAndAfterBinaryOperators: true, insertSpaceBeforeAndAfterBinaryOperators: true,
InsertSpaceAfterKeywordsInControlFlowStatements: true, insertSpaceAfterKeywordsInControlFlowStatements: true,
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false, insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true, insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true,
PlaceOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForFunctions: false,
PlaceOpenBraceOnNewLineForControlBlocks: false, placeOpenBraceOnNewLineForControlBlocks: false,
}; };
} }
} }
+15 -15
View File
@@ -217,22 +217,22 @@ function format(text:string): string {
function getDefaultOptions(): ts.FormatCodeOptions { function getDefaultOptions(): ts.FormatCodeOptions {
return { return {
IndentSize: 4, indentSize: 4,
TabSize: 4, tabSize: 4,
NewLineCharacter: '\r\n', newLineCharacter: '\r\n',
ConvertTabsToSpaces: true, convertTabsToSpaces: true,
IndentStyle: ts.IndentStyle.Block, indentStyle: ts.IndentStyle.Block,
InsertSpaceAfterCommaDelimiter: true, insertSpaceAfterCommaDelimiter: true,
InsertSpaceAfterSemicolonInForStatements: true, insertSpaceAfterSemicolonInForStatements: true,
InsertSpaceBeforeAndAfterBinaryOperators: true, insertSpaceBeforeAndAfterBinaryOperators: true,
InsertSpaceAfterKeywordsInControlFlowStatements: true, insertSpaceAfterKeywordsInControlFlowStatements: true,
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false, insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true, insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true,
PlaceOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForFunctions: false,
PlaceOpenBraceOnNewLineForControlBlocks: false, placeOpenBraceOnNewLineForControlBlocks: false,
}; };
} }
} }
+10
View File
@@ -141,6 +141,16 @@
"default": true, "default": true,
"description": "%html.suggest.html5.desc%" "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": { "html.trace.server": {
"type": "string", "type": "string",
"enum": [ "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.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.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.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 { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType } from 'vscode-languageserver';
import { DocumentContext } from 'vscode-html-languageservice'; 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 { getLanguageModes, LanguageModes } from './modes/languageModes';
import { format } from './modes/formatting';
import * as url from 'url'; import * as url from 'url';
import * as path from 'path'; import * as path from 'path';
import uri from 'vscode-uri'; 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. // The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration((change) => { connection.onDidChangeConfiguration((change) => {
settings = change.settings; 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 => { languageModes.getAllModes().forEach(m => {
if (m.configure) { if (m.configure) {
@@ -115,7 +126,7 @@ function triggerValidation(textDocument: TextDocument): void {
function validateTextDocument(textDocument: TextDocument): void { function validateTextDocument(textDocument: TextDocument): void {
let diagnostics: Diagnostic[] = []; let diagnostics: Diagnostic[] = [];
languageModes.getAllModesInDocument(textDocument).forEach(mode => { languageModes.getAllModesInDocument(textDocument).forEach(mode => {
if (mode.doValidation) { if (mode.doValidation && validation[mode.getId()]) {
pushAll(diagnostics, mode.doValidation(textDocument)); pushAll(diagnostics, mode.doValidation(textDocument));
} }
}); });
@@ -201,18 +212,11 @@ connection.onSignatureHelp(signatureHelpParms => {
connection.onDocumentRangeFormatting(formatParams => { connection.onDocumentRangeFormatting(formatParams => {
let document = documents.get(formatParams.textDocument.uri); 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 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 }; let enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) };
ranges.forEach(r => {
let mode = r.mode; return format(languageModes, document, formatParams.range, formatParams.options, enabledModes);
if (mode && mode.format && enabledModes[mode.getId()] && !r.attributeValue) {
let edits = mode.format(document, r, formatParams.options);
pushAll(result, edits);
}
});
return result;
}); });
connection.onDocumentLinks(documentLinkParam => { 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; settings = options && options.html;
}, },
doComplete(document: TextDocument, position: Position) { 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); return htmlLanguageService.doComplete(document, position, htmlDocuments.get(document), options);
}, },
doHover(document: TextDocument, position: Position) { doHover(document: TextDocument, position: Position) {
@@ -8,6 +8,8 @@ import * as assert from 'assert';
import { getLanguageModes } from '../modes/languageModes'; import { getLanguageModes } from '../modes/languageModes';
import { TextDocument, Range, TextEdit, FormattingOptions } from 'vscode-languageserver-types'; import { TextDocument, Range, TextEdit, FormattingOptions } from 'vscode-languageserver-types';
import { format } from '../modes/formatting';
suite('HTML Embedded Formatting', () => { suite('HTML Embedded Formatting', () => {
function assertFormat(value: string, expected: string, options?: any): void { 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 range = Range.create(document.positionAt(rangeStartOffset), document.positionAt(rangeEndOffset));
let formatOptions = FormattingOptions.create(2, true); let formatOptions = FormattingOptions.create(2, true);
let ranges = languageModes.getModesInRange(document, range); let result = format(languageModes, document, range, formatOptions, { css: true, javascript: true });
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 actual = applyEdits(document, result); let actual = applyEdits(document, result);
assert.equal(actual, expected); assert.equal(actual, expected);
} }
@@ -52,38 +47,38 @@ suite('HTML Embedded Formatting', () => {
test('HTML & Scripts', function (): any { 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></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>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</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 </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 </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>'); 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 { 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 { 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 { 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 { test('EndWithNewline', function (): any {
let options = { let options = {
html: { html: {
format: { 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>\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>|<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", "c": "````",
"t": "markdown.meta.paragraph", "t": "block.definition.fenced_code.markdown.markup.punctuation",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1594,9 +1594,20 @@
"hc_black": ".hc-black .token rgb(255, 255, 255)" "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\"] }", "c": " { value: [\"or with a mime type\"] }",
"t": "markdown.meta.paragraph", "t": "block.fenced_code.markdown.markup",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1607,7 +1618,7 @@
}, },
{ {
"c": "````", "c": "````",
"t": "markdown.meta.paragraph", "t": "block.definition.fenced_code.markdown.markup.punctuation",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1827,7 +1838,7 @@
}, },
{ {
"c": "~~~", "c": "~~~",
"t": "markdown.meta.paragraph", "t": "block.definition.fenced_code.markdown.markup.punctuation",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1838,7 +1849,7 @@
}, },
{ {
"c": "// Markdown extra adds un-indented code blocks too", "c": "// Markdown extra adds un-indented code blocks too",
"t": "markdown.meta.paragraph", "t": "block.fenced_code.markdown.markup",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1848,74 +1859,8 @@
} }
}, },
{ {
"c": "if (this", "c": "if (this_is_more_code == true && !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)",
"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",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1926,7 +1871,7 @@
}, },
{ {
"c": " // tild wrapped code blocks, also not indented", "c": " // tild wrapped code blocks, also not indented",
"t": "markdown.meta.paragraph", "t": "block.fenced_code.markdown.markup",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1937,7 +1882,7 @@
}, },
{ {
"c": "}", "c": "}",
"t": "markdown.meta.paragraph", "t": "block.fenced_code.markdown.markup",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
@@ -1948,7 +1893,7 @@
}, },
{ {
"c": "~~~", "c": "~~~",
"t": "markdown.meta.paragraph", "t": "block.definition.fenced_code.markdown.markup.punctuation",
"r": { "r": {
"dark_plus": ".vs-dark .token rgb(212, 212, 212)", "dark_plus": ".vs-dark .token rgb(212, 212, 212)",
"light_plus": ".vs .token rgb(0, 0, 0)", "light_plus": ".vs .token rgb(0, 0, 0)",
+5
View File
@@ -99,6 +99,11 @@
"default": true, "default": true,
"description": "%typescript.check.tscVersion%" "description": "%typescript.check.tscVersion%"
}, },
"typescript.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%typescript.referencesCodeLens.enabled%"
},
"typescript.tsserver.trace": { "typescript.tsserver.trace": {
"type": "string", "type": "string",
"enum": [ "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.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.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.", "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'; '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 * as Proto from '../protocol';
import { ITypescriptServiceClient } from '../typescriptService'; 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[]> { public provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): Thenable<Command[]> {
const file = this.client.asAbsolutePath(document.uri); const file = this.client.asAbsolutePath(document.uri);
if (!file) { if (!file) {
return Promise.resolve(null); return Promise.resolve<Command[]>([]);
} }
const source: Source = { const source: Source = {
@@ -99,11 +99,29 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider
private onCodeAction(source: Source, workspaceEdit: WorkspaceEdit) { private onCodeAction(source: Source, workspaceEdit: WorkspaceEdit) {
workspace.applyEdit(workspaceEdit).then(success => { workspace.applyEdit(workspaceEdit).then(success => {
if (!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 // TODO: Workaround for https://github.com/Microsoft/TypeScript/issues/12249
// apply formatting to the source range until TS returns formatted results // 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) { if (!edits || !edits.length) {
return false; return false;
} }
@@ -119,7 +119,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise<CompletionItem[]> { public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise<CompletionItem[]> {
if (this.typingsStatus.isAcquiringTypings) { if (this.typingsStatus.isAcquiringTypings) {
return Promise.reject({ return Promise.reject<CompletionItem[]>({
label: localize('acquiringTypingsLabel', 'Acquiring typings...'), label: localize('acquiringTypingsLabel', 'Acquiring typings...'),
detail: localize('acquiringTypingsDetail', 'Acquiring typings definitions for IntelliSense.') 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 // Don't complete function calls inside of destructive assigments or imports
return this.client.execute('quickinfo', args).then(infoResponse => { return this.client.execute('quickinfo', args).then(infoResponse => {
const info = infoResponse.body; const info = infoResponse.body;
console.log(info && info.kind);
switch (info && info.kind) { switch (info && info.kind) {
case 'var': case 'var':
case 'let': 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 CompletionItemProvider from './features/completionItemProvider';
import WorkspaceSymbolProvider from './features/workspaceSymbolProvider'; import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider'; import CodeActionProvider from './features/codeActionProvider';
import ReferenceCodeLensProvider from './features/referencesCodeLensProvider';
import * as BuildStatus from './utils/buildStatus'; import * as BuildStatus from './utils/buildStatus';
import * as ProjectStatus from './utils/projectStatus'; import * as ProjectStatus from './utils/projectStatus';
@@ -107,6 +108,7 @@ class LanguageProvider {
private formattingProvider: FormattingProvider; private formattingProvider: FormattingProvider;
private formattingProviderRegistration: Disposable | null; private formattingProviderRegistration: Disposable | null;
private typingsStatus: TypingsStatus; private typingsStatus: TypingsStatus;
private referenceCodeLensProvider: ReferenceCodeLensProvider;
private _validate: boolean; private _validate: boolean;
@@ -156,6 +158,12 @@ class LanguageProvider {
this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider); 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 => { this.description.modeIds.forEach(modeId => {
let selector: DocumentFilter = { scheme: 'file', language: modeId }; let selector: DocumentFilter = { scheme: 'file', language: modeId };
languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.'); languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.');
@@ -171,6 +179,7 @@ class LanguageProvider {
if (client.apiVersion.has213Features()) { if (client.apiVersion.has213Features()) {
languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId)); languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId));
} }
languages.setLanguageConfiguration(modeId, { languages.setLanguageConfiguration(modeId, {
indentationRules: { indentationRules: {
// ^(.*\*/)?\s*\}.*$ // ^(.*\*/)?\s*\}.*$
@@ -217,6 +226,9 @@ class LanguageProvider {
if (this.completionItemProvider) { if (this.completionItemProvider) {
this.completionItemProvider.updateConfiguration(config); this.completionItemProvider.updateConfiguration(config);
} }
if (this.referenceCodeLensProvider) {
this.referenceCodeLensProvider.updateConfiguration(config);
}
if (this.formattingProvider) { if (this.formattingProvider) {
this.formattingProvider.updateConfiguration(config); this.formattingProvider.updateConfiguration(config);
if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) { if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {
@@ -346,17 +346,20 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
switch (selected.id) { switch (selected.id) {
case MessageAction.useLocal: case MessageAction.useLocal:
let pathValue = './node_modules/typescript/lib'; let pathValue = './node_modules/typescript/lib';
tsConfig.update('tsdk', pathValue, false); tsConfig.update('tsdk', pathValue, false).then(
window.showInformationMessage(localize('updatedtsdk', 'Updated workspace setting \'typescript.tsdk\' to {0}', pathValue)); () => 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; showVersionStatusItem = true;
return localModulePath; return localModulePath;
case MessageAction.useBundled: case MessageAction.useBundled:
tsConfig.update(checkWorkspaceVersionKey, false, false); tsConfig.update(checkWorkspaceVersionKey, false, false).then(
window.showInformationMessage(localize('updateLocalWorkspaceCheck', 'Updated workspace setting \'typescript.check.workspaceVersion\' to false')); () => 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; return modulePath;
case MessageAction.neverCheckLocalVersion: case MessageAction.neverCheckLocalVersion:
window.showInformationMessage(localize('updateGlobalWorkspaceCheck', 'Updated user setting \'typescript.check.workspaceVersion\' to false')); tsConfig.update(checkWorkspaceVersionKey, false, true).then(
tsConfig.update(checkWorkspaceVersionKey, false, true); () => 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; return modulePath;
default: default:
return modulePath; return modulePath;
+5 -5
View File
@@ -2,7 +2,7 @@
"name": "code-oss-dev", "name": "code-oss-dev",
"version": "1.9.0", "version": "1.9.0",
"electronVersion": "1.4.6", "electronVersion": "1.4.6",
"distro": "2eecc8b68318fba1fc5b62930287914ac9225e8a", "distro": "ef07477c3bbf2aa2f274b13093cbe0d96fa59fdd",
"author": { "author": {
"name": "Microsoft Corporation" "name": "Microsoft Corporation"
}, },
@@ -48,6 +48,7 @@
"eslint": "^3.4.0", "eslint": "^3.4.0",
"event-stream": "^3.1.7", "event-stream": "^3.1.7",
"express": "^4.13.1", "express": "^4.13.1",
"flatpak-bundler": "^0.1.1",
"ghooks": "1.0.3", "ghooks": "1.0.3",
"glob": "^5.0.13", "glob": "^5.0.13",
"gulp": "^3.8.9", "gulp": "^3.8.9",
@@ -59,6 +60,7 @@
"gulp-cssnano": "^2.1.0", "gulp-cssnano": "^2.1.0",
"gulp-filter": "^3.0.0", "gulp-filter": "^3.0.0",
"gulp-flatmap": "^1.0.0", "gulp-flatmap": "^1.0.0",
"gulp-image-resize": "^0.10.0",
"gulp-json-editor": "^2.2.1", "gulp-json-editor": "^2.2.1",
"gulp-mocha": "^2.1.3", "gulp-mocha": "^2.1.3",
"gulp-remote-src": "^0.4.0", "gulp-remote-src": "^0.4.0",
@@ -71,8 +73,6 @@
"gulp-uglify": "^2.0.0", "gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.6", "gulp-util": "^3.0.6",
"gulp-vinyl-zip": "^1.2.2", "gulp-vinyl-zip": "^1.2.2",
"gulp-image-resize": "^0.10.0",
"flatpak-bundler": "^0.1.1",
"innosetup-compiler": "^5.5.60", "innosetup-compiler": "^5.5.60",
"is": "^3.1.0", "is": "^3.1.0",
"istanbul": "^0.3.17", "istanbul": "^0.3.17",
@@ -91,8 +91,8 @@
"sinon": "^1.17.2", "sinon": "^1.17.2",
"source-map": "^0.4.4", "source-map": "^0.4.4",
"tslint": "^3.3.0", "tslint": "^3.3.0",
"typescript": "2.0.3", "typescript": "^2.1.4",
"typescript-formatter": "3.1.0", "typescript-formatter": "4.0.1",
"uglify-js": "2.4.8", "uglify-js": "2.4.8",
"underscore": "^1.8.2", "underscore": "^1.8.2",
"vinyl": "^0.4.5", "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. # Licensed under the MIT License. See License.txt in the project root for license information.
# If root, ensure that --user-data-dir is specified # If root, ensure that --user-data-dir is specified
ARGS=$@
if [ "$(id -u)" = "0" ]; then if [ "$(id -u)" = "0" ]; then
while test $# -gt 0 while test $# -gt 0
do do
@@ -34,5 +33,5 @@ fi
ELECTRON="$VSCODE_PATH/@@NAME@@" ELECTRON="$VSCODE_PATH/@@NAME@@"
CLI="$VSCODE_PATH/resources/app/out/cli.js" CLI="$VSCODE_PATH/resources/app/out/cli.js"
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" $ARGS ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@"
exit $? exit $?
+1 -1
View File
@@ -2,7 +2,7 @@
Name=@@NAME_LONG@@ Name=@@NAME_LONG@@
Comment=Code Editing. Redefined. Comment=Code Editing. Redefined.
GenericName=Text Editor GenericName=Text Editor
Exec=/usr/share/@@NAME@@/@@NAME@@ %U Exec=/usr/share/@@NAME@@/@@NAME@@ --new-window-if-not-first %U
Icon=@@NAME@@ Icon=@@NAME@@
Type=Application Type=Application
StartupNotify=true StartupNotify=true
+1
View File
@@ -180,6 +180,7 @@ var nodeCachedDataDir = getNodeCachedDataDir().then(function (value) {
// Load our code once ready // Load our code once ready
app.once('ready', function () { app.once('ready', function () {
global.perfAppReady = Date.now();
var nlsConfig = getNLSConfiguration(); var nlsConfig = getNLSConfiguration();
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); 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>; (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 fn = e => emitter.fire(e);
const emitter = new Emitter<any>({ const emitter = new Emitter<any>({
onFirstListenerAdd: () => { onFirstListenerAdd: () => {
element.addEventListener(type, fn); element.addEventListener(type, fn, useCapture);
}, },
onLastListenerRemove: () => { 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(); const renderer = new marked.Renderer();
renderer.image = (href: string, title: string, text: string) => { renderer.image = (href: string, title: string, text: string) => {
let dimensions = []; let dimensions: string[] = [];
if (href) { if (href) {
const splitted = href.split('|').map(s => s.trim()); const splitted = href.split('|').map(s => s.trim());
href = splitted[0]; href = splitted[0];
+4 -3
View File
@@ -7,7 +7,7 @@ import 'vs/css!./list';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { range } from 'vs/base/common/arrays'; import { range } from 'vs/base/common/arrays';
import { IDelegate, IRenderer, IFocusChangeEvent, ISelectionChangeEvent } from './list'; import { IDelegate, IRenderer, IFocusChangeEvent, ISelectionChangeEvent } from './list';
import { List } from './listWidget'; import { List, IListOptions } from './listWidget';
import { IPagedModel } from 'vs/base/common/paging'; import { IPagedModel } from 'vs/base/common/paging';
import Event, { mapEvent } from 'vs/base/common/event'; import Event, { mapEvent } from 'vs/base/common/event';
@@ -67,10 +67,11 @@ export class PagedList<T> {
constructor( constructor(
container: HTMLElement, container: HTMLElement,
delegate: IDelegate<number>, 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)); 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>> { 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); 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) { if (index < 0) {
return; return;
} }
+19 -5
View File
@@ -65,7 +65,7 @@ class Trait<T> implements IDisposable {
splice(start: number, deleteCount: number, insertCount: number): void { splice(start: number, deleteCount: number, insertCount: number): void {
const diff = insertCount - deleteCount; const diff = insertCount - deleteCount;
const end = start + deleteCount; const end = start + deleteCount;
const indexes = []; const indexes: number[] = [];
for (let index of indexes) { for (let index of indexes) {
if (index >= start && index < end) { if (index >= start && index < end) {
@@ -110,13 +110,13 @@ class Trait<T> implements IDisposable {
class FocusTrait<T> extends Trait<T> { class FocusTrait<T> extends Trait<T> {
constructor(private getElementId: (number) => string) { constructor(private getElementId: (number: number) => string) {
super('focused'); super('focused');
} }
renderElement(element: T, index: number, container: HTMLElement): void { renderElement(element: T, index: number, container: HTMLElement): void {
super.renderElement(element, index, container); super.renderElement(element, index, container);
container.setAttribute('role', 'option'); container.setAttribute('role', 'treeitem');
container.setAttribute('id', this.getElementId(index)); container.setAttribute('id', this.getElementId(index));
} }
} }
@@ -201,6 +201,7 @@ class Controller<T> implements IDisposable {
} }
export interface IListOptions extends IListViewOptions { export interface IListOptions extends IListViewOptions {
ariaLabel?: string;
} }
const DefaultOptions: IListOptions = {}; const DefaultOptions: IListOptions = {};
@@ -245,13 +246,17 @@ export class List<T> implements IDisposable {
}); });
this.view = new ListView(container, delegate, renderers, options); 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.view.domNode.tabIndex = 0;
this.controller = new Controller(this, this.view); this.controller = new Controller(this, this.view);
this.disposables = [this.focus, this.selection, this.view, this.controller]; this.disposables = [this.focus, this.selection, this.view, this.controller];
this._onDOMFocus = domEvent(this.view.domNode, 'focus'); this._onDOMFocus = domEvent(this.view.domNode, 'focus');
this.onFocusChange(this._onFocusChange, this, this.disposables); 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 { splice(start: number, deleteCount: number, ...elements: T[]): void {
@@ -418,7 +423,16 @@ export class List<T> implements IDisposable {
} }
private _onFocusChange(): void { 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 { dispose(): void {
+4 -4
View File
@@ -25,22 +25,22 @@
cursor: default !important; cursor: default !important;
} }
.vertical-cursor-container * { .vertical-cursor-container {
cursor: ew-resize; cursor: ew-resize;
} }
.horizontal-cursor-container * { .horizontal-cursor-container {
cursor: ns-resize; cursor: ns-resize;
} }
/** Custom Mac Cursor */ /** Custom Mac Cursor */
.monaco-sash.mac.vertical, .monaco-sash.mac.vertical,
.vertical-cursor-container-mac * { .vertical-cursor-container-mac {
cursor: col-resize; cursor: col-resize;
} }
.monaco-sash.mac.horizontal, .monaco-sash.mac.horizontal,
.horizontal-cursor-container-mac * { .horizontal-cursor-container-mac {
cursor: row-resize; 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; 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])) { if (!itemEquals(one[i], other[i])) {
return false; 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 { export function commonPrefixLength<T>(one: T[], other: T[], equals: (a: T, b: T) => boolean = (a, b) => a === b): number {
let result = 0; 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++; 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 * The throttler implements this via the queue() method, by providing it a task
* factory. Following the example: * factory. Following the example:
* *
* var throttler = new Throttler(); * const throttler = new Throttler();
* var letters = []; * const letters = [];
* *
* function deliver() { * function deliver() {
* const lettersToDeliver = letters; * 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 * to be executed and the waiting period (delay) must be passed in as arguments. Following
* the example: * the example:
* *
* var delayer = new Delayer(WAITING_PERIOD); * const delayer = new Delayer(WAITING_PERIOD);
* var letters = []; * const letters = [];
* *
* function letterReceived(l) { * function letterReceived(l) {
* letters.push(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> { export function first<T>(promiseFactories: ITask<TPromise<T>>[], shouldStop: (t: T) => boolean = t => !!t): TPromise<T> {
promiseFactories = [...promiseFactories.reverse()]; promiseFactories = [...promiseFactories.reverse()];
const loop = () => { const loop: () => TPromise<T> = () => {
if (promiseFactories.length === 0) { if (promiseFactories.length === 0) {
return TPromise.as(null); 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 s = Math.min(hsla.s, 1);
let l = Math.min(hsla.l, 1); let l = Math.min(hsla.l, 1);
let a = hsla.a === void 0 ? hsla.a : 1; let a = hsla.a === void 0 ? hsla.a : 1;
let r, g, b; let r: number, g: number, b: number;
if (s === 0) { if (s === 0) {
r = g = b = l; // achromatic r = g = b = l; // achromatic
} else { } else {
let hue2rgb = function hue2rgb(p, q, t) { let hue2rgb = function hue2rgb(p: number, q: number, t: number) {
if (t < 0) { if (t < 0) {
t += 1; t += 1;
} }
@@ -115,7 +115,7 @@ export class Color {
* Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white. * Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white.
*/ */
public getLuminosity(): number { public getLuminosity(): number {
let luminosityFor = function (color): number { let luminosityFor = function (color: number): number {
let c = color / 255; let c = color / 255;
return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4); 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> { export interface IScorableResourceAccessor<T> {
getLabel(T): string; getLabel(t: T): string;
getResourcePath(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 { 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}`; const memoizeKey = `$memoize$${key}`;
descriptor[fnKey] = function (...args) { descriptor[fnKey] = function (...args: any[]) {
if (!this.hasOwnProperty(memoizeKey)) { if (!this.hasOwnProperty(memoizeKey)) {
Object.defineProperty(this, memoizeKey, { Object.defineProperty(this, memoizeKey, {
configurable: false, configurable: false,
+1 -1
View File
@@ -19,7 +19,7 @@ globals.Monaco.Diagnostics = {};
var switches = globals.Monaco.Diagnostics; var switches = globals.Monaco.Diagnostics;
var map = {}; var map = {};
var data = []; var data: any[] = [];
function fifo(array: any[], size: number) { function fifo(array: any[], size: number) {
while (array.length > size) { while (array.length > size) {
+2 -2
View File
@@ -148,8 +148,8 @@ export class LcsDiff2 {
// Construct the changes // Construct the changes
let i = 0; let i = 0;
let j = 0; let j = 0;
let xChangeStart, yChangeStart; let xChangeStart: number, yChangeStart: number;
let changes = []; let changes: DiffChange[] = [];
while (i < xLength && j < yLength) { while (i < xLength && j < yLength) {
if (this.resultX[i] && this.resultY[j]) { if (this.resultX[i] && this.resultY[j]) {
// No change // 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> { export function any<T>(...events: Event<T>[]): Event<T> {
let listeners = []; let listeners: IDisposable[] = [];
const emitter = new Emitter<T>({ const emitter = new Emitter<T>({
onFirstListenerAdd() { onFirstListenerAdd() {
@@ -297,7 +297,7 @@ export class EventBufferer {
} }
bufferEvents(fn: () => void): void { bufferEvents(fn: () => void): void {
const buffer = []; const buffer: Function[] = [];
this.buffers.push(buffer); this.buffers.push(buffer);
fn(); fn();
this.buffers.pop(); this.buffers.pop();
@@ -334,7 +334,7 @@ class ChainableEvent<T> implements IChainableEvent<T> {
return new ChainableEvent(filterEvent(this._event, fn)); return new ChainableEvent(filterEvent(this._event, fn));
} }
on(listener, thisArgs, disposables) { on(listener, thisArgs, disposables: IDisposable[]) {
return this._event(listener, thisArgs, disposables); return this._event(listener, thisArgs, disposables);
} }
} }
+11 -8
View File
@@ -209,7 +209,7 @@ function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis {
} }
function isUpperCaseWord(analysis: ICamelCaseAnalysis): boolean { function isUpperCaseWord(analysis: ICamelCaseAnalysis): boolean {
const { upperPercent, lowerPercent, alphaPercent, numericPercent } = analysis; const { upperPercent, lowerPercent } = analysis;
return lowerPercent === 0 && upperPercent > 0.6; 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 // 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) // 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) { if (!target || target.length === 0) {
return null; return null;
} }
@@ -283,14 +284,14 @@ export function matchesWords(word: string, target: string): IMatch[] {
let result: IMatch[] = null; let result: IMatch[] = null;
let i = 0; 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); i = nextWord(target, i + 1);
} }
return result; 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) { if (i === word.length) {
return []; return [];
} else if (j === target.length) { } 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()) { } else if (word[i] !== target[j].toLowerCase()) {
return null; return null;
} else { } else {
let result = null; let result: IMatch[] = null;
let nextWordIndex = j + 1; 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) { while (!result && (nextWordIndex = nextWord(target, nextWordIndex)) < target.length) {
result = _matchesWords(word, target, i + 1, nextWordIndex); result = _matchesWords(word, target, i + 1, nextWordIndex, contiguous);
nextWordIndex++; nextWordIndex++;
} }
}
return result === null ? null : join({ start: j, end: j + 1 }, result); 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. * 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. * Read the next token. Returns the tolen code.
*/ */
+15 -10
View File
@@ -4,8 +4,6 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
'use strict'; 'use strict';
import { isArray } from './types';
export const empty: IDisposable = Object.freeze({ export const empty: IDisposable = Object.freeze({
dispose() { } dispose() { }
}); });
@@ -14,17 +12,24 @@ export interface IDisposable {
dispose(): void; 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[];
export function dispose<T extends IDisposable>(...disposables: T[]): T[] { export function dispose<T extends IDisposable>(first: T | T[], ...rest: T[]): T | T[] {
const first = disposables[0];
if (isArray(first)) { if (Array.isArray(first)) {
disposables = first as any as T[]; first.forEach(d => d && d.dispose());
}
disposables.forEach(d => d && d.dispose());
return []; return [];
} else if (rest.length === 0) {
if (first) {
first.dispose();
return first;
}
} else {
dispose(first);
dispose(rest);
return [];
}
} }
export function combinedDisposable(disposables: IDisposable[]): IDisposable { export function combinedDisposable(disposables: IDisposable[]): IDisposable {
+4 -4
View File
@@ -52,7 +52,7 @@ export class LinkedMap<K extends Key, T> {
} }
public keys(): K[] { public keys(): K[] {
var keys: K[] = []; const keys: K[] = [];
for (let key in this.map) { for (let key in this.map) {
keys.push(this.map[key].key); keys.push(this.map[key].key);
} }
@@ -60,7 +60,7 @@ export class LinkedMap<K extends Key, T> {
} }
public values(): T[] { public values(): T[] {
var values: T[] = []; const values: T[] = [];
for (let key in this.map) { for (let key in this.map) {
values.push(this.map[key].value); values.push(this.map[key].value);
} }
@@ -68,7 +68,7 @@ export class LinkedMap<K extends Key, T> {
} }
public entries(): Entry<K, T>[] { public entries(): Entry<K, T>[] {
var entries: Entry<K, T>[] = []; const entries: Entry<K, T>[] = [];
for (let key in this.map) { for (let key in this.map) {
entries.push(this.map[key]); entries.push(this.map[key]);
} }
@@ -310,7 +310,7 @@ class Node<E> {
*/ */
export class TrieMap<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 _splitter: (s: string) => string[];
private _root = new Node<E>(); 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 patternMatch: ITextMimeAssociationItem;
let extensionMatch: ITextMimeAssociationItem; let extensionMatch: ITextMimeAssociationItem;
for (var i = 0; i < associations.length; i++) { for (let i = 0; i < associations.length; i++) {
let association = associations[i]; let association = associations[i];
// First exact name match // First exact name match
@@ -243,7 +243,7 @@ export function isUnspecific(mime: string[] | string): boolean {
} }
export function suggestFilename(langId: string, prefix: string): string { 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]; let association = registeredAssociations[i];
if (association.userConfigured) { if (association.userConfigured) {
continue; // only support registered ones 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 * A schema that is used for models that exist in memory
* only and that have no correspondence on a server or such. * 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 * 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 * 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 { export interface IXHROptions {
+5 -5
View File
@@ -11,12 +11,12 @@ import { CharCode } from 'vs/base/common/charCode';
/** /**
* The forward slash path separator. * The forward slash path separator.
*/ */
export var sep = '/'; export const sep = '/';
/** /**
* The native path separator depending on the OS. * The native path separator depending on the OS.
*/ */
export var nativeSep = isWindows ? '\\' : '/'; export const nativeSep = isWindows ? '\\' : '/';
export function relative(from: string, to: string): string { export function relative(from: string, to: string): string {
const originalNormalizedFrom = normalize(from); const originalNormalizedFrom = normalize(from);
@@ -50,7 +50,7 @@ export function relative(from: string, to: string): string {
* @returns the directory name of a path. * @returns the directory name of a path.
*/ */
export function dirname(path: string): string { export function dirname(path: string): string {
var idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\');
if (idx === 0) { if (idx === 0) {
return '.'; return '.';
} else if (~idx === 0) { } else if (~idx === 0) {
@@ -64,7 +64,7 @@ export function dirname(path: string): string {
* @returns the base name of a path. * @returns the base name of a path.
*/ */
export function basename(path: string): string { export function basename(path: string): string {
var idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\');
if (idx === 0) { if (idx === 0) {
return path; return path;
} else if (~idx === path.length - 1) { } else if (~idx === path.length - 1) {
@@ -79,7 +79,7 @@ export function basename(path: string): string {
*/ */
export function extname(path: string): string { export function extname(path: string): string {
path = basename(path); path = basename(path);
var idx = ~path.lastIndexOf('.'); const idx = ~path.lastIndexOf('.');
return idx ? path.substring(~idx) : ''; return idx ? path.substring(~idx) : '';
} }
+3 -3
View File
@@ -13,8 +13,8 @@ let _isRootUser = false;
let _isNative = false; let _isNative = false;
let _isWeb = false; let _isWeb = false;
let _isQunit = false; let _isQunit = false;
let _locale = undefined; let _locale: string = undefined;
let _language = undefined; let _language: string = undefined;
interface NLSConfig { interface NLSConfig {
locale: string; locale: string;
@@ -124,7 +124,7 @@ interface IGlobals {
clearTimeout(token: TimeoutToken): void; clearTimeout(token: TimeoutToken): void;
setInterval(callback: (...args: any[]) => void, delay: number, ...args: any[]): IntervalToken; 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); const _globals = <IGlobals>(typeof self === 'object' ? self : global);
+1 -1
View File
@@ -6,7 +6,7 @@
import { globals } from 'vs/base/common/platform'; 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 { 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 { export function repeat(s: string, count: number): string {
var result = ''; let result = '';
for (var i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
result += s; result += s;
} }
return result; 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> { export function buffer(zipPath: string, filePath: string): TPromise<Buffer> {
return read(zipPath, filePath).then(stream => { return read(zipPath, filePath).then(stream => {
return new TPromise<Buffer>((c, e) => { return new TPromise<Buffer>((c, e) => {
const buffers = []; const buffers: Buffer[] = [];
stream.once('error', e); stream.once('error', e);
stream.on('data', b => buffers.push(b)); stream.on('data', b => buffers.push(b));
stream.on('end', () => c(Buffer.concat(buffers))); stream.on('end', () => c(Buffer.concat(buffers)));
@@ -689,15 +689,15 @@ export class QuickOpenModel implements
return this._entries; return this._entries;
} }
getId(entry: QuickOpenEntry): string { public getId(entry: QuickOpenEntry): string {
return entry.getId(); return entry.getId();
} }
getLabel(entry: QuickOpenEntry): string { public getLabel(entry: QuickOpenEntry): string {
return entry.getLabel(); return entry.getLabel();
} }
getAriaLabel(entry: QuickOpenEntry): string { public getAriaLabel(entry: QuickOpenEntry): string {
const ariaLabel = entry.getAriaLabel(); const ariaLabel = entry.getAriaLabel();
if (ariaLabel) { if (ariaLabel) {
return nls.localize('quickOpenAriaLabelEntry', "{0}, picker", entry.getAriaLabel()); return nls.localize('quickOpenAriaLabelEntry', "{0}, picker", entry.getAriaLabel());
@@ -706,11 +706,11 @@ export class QuickOpenModel implements
return nls.localize('quickOpenAriaLabel', "picker"); return nls.localize('quickOpenAriaLabel', "picker");
} }
isVisible(entry: QuickOpenEntry): boolean { public isVisible(entry: QuickOpenEntry): boolean {
return !entry.isHidden(); 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); 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 * Returns a navigator which allows to discover the visible and
* expanded elements in the tree. * expanded elements in the tree.
*/ */
getNavigator(): INavigator<any>; getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator<any>;
/** /**
* Disposes the tree * 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 { 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 { 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); return this.model.hasTrait(trait, element);
} }
getNavigator(): INavigator<any> { getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator<any> {
return new MappedNavigator(this.model.getNavigator(), i => i && i.getElement()); return new MappedNavigator(this.model.getNavigator(fromElement, subTreeOnly), i => i && i.getElement());
} }
public dispose(): void { 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, '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 }]); 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> { export class DeferredTPromise<T> extends TPromise<T> {
public canceled = false; public canceled: boolean;
private completeCallback: TValueCallback<T>; private completeCallback: TValueCallback<T>;
private errorCallback: (err: any) => void; private errorCallback: (err: any) => void;
private progressCallback: ProgressCallback; private progressCallback: ProgressCallback;
constructor() { constructor() {
let captured: any;
super((c, e, p) => { super((c, e, p) => {
this.completeCallback = c; captured = { c, e, p };
this.errorCallback = e;
this.progressCallback = p;
}, () => this.oncancel()); }, () => this.oncancel());
this.canceled = false;
this.completeCallback = captured.c;
this.errorCallback = captured.e;
this.progressCallback = captured.p;
} }
public complete(value: T) { public complete(value: T) {
@@ -51,7 +54,13 @@ export class DeferredPPromise<C, P> extends PPromise<C, P> {
private progressCallback: TProgressCallback<P>; private progressCallback: TProgressCallback<P>;
constructor(init: (complete: TValueCallback<C>, error: (err: any) => void, progress: TProgressCallback<P>) => void = (c, e, p) => { }, oncancel?: any) { 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 { private oncancel(): void {
+9 -6
View File
@@ -5,7 +5,7 @@
'use strict'; '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 { VSCodeWindow } from 'vs/code/electron-main/window';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IChannel } from 'vs/base/parts/ipc/common/ipc';
@@ -82,6 +82,7 @@ export class LaunchService implements ILaunchService {
const openUrlArg = args['open-url'] || []; const openUrlArg = args['open-url'] || [];
const openUrl = typeof openUrlArg === 'string' ? [openUrlArg] : openUrlArg; const openUrl = typeof openUrlArg === 'string' ? [openUrlArg] : openUrlArg;
const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.OTHER;
if (openUrl.length > 0) { if (openUrl.length > 0) {
openUrl.forEach(url => this.urlService.open(url)); openUrl.forEach(url => this.urlService.open(url));
@@ -91,17 +92,19 @@ export class LaunchService implements ILaunchService {
// Otherwise handle in windows service // Otherwise handle in windows service
let usedWindows: VSCodeWindow[]; let usedWindows: VSCodeWindow[];
if (!!args.extensionDevelopmentPath) { if (!!args.extensionDevelopmentPath) {
this.windowsService.openExtensionDevelopmentHostWindow({ cli: args, userEnv }); this.windowsService.openExtensionDevelopmentHostWindow({ context, cli: args, userEnv });
} else if (args._.length === 0 && args['new-window']) { } else if (args._.length === 0 && args['new-window'] || args['new-window-if-not-first']) {
usedWindows = this.windowsService.open({ cli: args, userEnv, forceNewWindow: true, forceEmpty: true }); usedWindows = this.windowsService.open({ context, cli: args, userEnv, forceNewWindow: true, forceEmpty: true });
} else if (args._.length === 0) { } else if (args._.length === 0) {
usedWindows = [this.windowsService.focusLastActive(args)]; usedWindows = [this.windowsService.focusLastActive(args, context)];
} else { } else {
usedWindows = this.windowsService.open({ usedWindows = this.windowsService.open({
context,
cli: args, cli: args,
userEnv, userEnv,
forceNewWindow: args.wait || args['new-window'], forceNewWindow: args.wait || args['new-window'] || args['new-window-if-not-first'],
preferNewWindow: !args['reuse-window'], preferNewWindow: !args['reuse-window'],
forceReuseWindow: args['reuse-window'],
diffMode: args.diff 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 { parseMainProcessArgv } from 'vs/platform/environment/node/argv';
import { mkdirp } from 'vs/base/node/pfs'; import { mkdirp } from 'vs/base/node/pfs';
import { validatePaths } from 'vs/code/electron-main/paths'; 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 { IWindowsService } from 'vs/platform/windows/common/windows';
import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
@@ -251,12 +251,13 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
windowsMainService.ready(userEnv); windowsMainService.ready(userEnv);
// Open our first window // Open our first window
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.OTHER;
if (environmentService.args['new-window'] && environmentService.args._.length === 0) { 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)) { } 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 { } 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 // 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 * as arrays from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem } from 'electron'; 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 { VSCodeWindow } from 'vs/code/electron-main/window';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageService } from 'vs/code/electron-main/storage'; import { IStorageService } from 'vs/code/electron-main/storage';
@@ -315,7 +315,7 @@ export class VSCodeMenu {
this.appMenuInstalled = true; this.appMenuInstalled = true;
const dockMenu = new Menu(); 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); app.dock.setMenu(dockMenu);
} }
@@ -351,19 +351,19 @@ export class VSCodeMenu {
let newFile: Electron.MenuItem; let newFile: Electron.MenuItem;
if (hasNoWindows) { 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 { } else {
newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File"), 'workbench.action.files.newUntitledFile'); 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 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: () => this.windowsService.openFolderPicker() })); 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; let openFile: Electron.MenuItem;
if (hasNoWindows) { 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 { } 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(); const openRecentMenu = new Menu();
@@ -379,7 +379,7 @@ export class VSCodeMenu {
const preferences = this.getPreferencesMenu(); 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 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 })); 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 { 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, { return new MenuItem(this.likeAction(actionId, {
label: unMnemonicLabel(path), click: (menuItem, win, event) => { label: unMnemonicLabel(label), click: (menuItem, win, event) => {
const openInNewWindow = event && ((!platform.isMacintosh && event.ctrlKey) || (platform.isMacintosh && event.metaKey)); const openInNewWindow = this.isOptionClick(event);
const success = !!this.windowsService.open({ cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow }); const success = !!this.windowsService.open({ context: OpenContext.MENU, cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow });
if (!success) { if (!success) {
this.windowsService.removeFromRecentPathsList(path); this.windowsService.removeFromRecentPathsList(path);
} }
@@ -479,6 +484,10 @@ export class VSCodeMenu {
}, false)); }, 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 { private createRoleMenuItem(label: string, actionId: string, role: Electron.MenuItemRole): Electron.MenuItem {
const options: Electron.MenuItemOptions = { const options: Electron.MenuItemOptions = {
label: mnemonicLabel(label), 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(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem;
private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem { private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem {
const label = mnemonicLabel(arg1); 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 enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0;
const checked = typeof arg4 === 'boolean' ? arg4 : false; const checked = typeof arg4 === 'boolean' ? arg4 : false;
+22 -16
View File
@@ -82,6 +82,7 @@ export interface IWindowConfiguration extends ParsedArgs {
isInitialStartup?: boolean; isInitialStartup?: boolean;
perfStartTime?: number; perfStartTime?: number;
perfAppReady?: number;
perfWindowLoadTime?: number; perfWindowLoadTime?: number;
workspacePath?: string; workspacePath?: string;
@@ -205,21 +206,6 @@ export class VSCodeWindow implements IVSCodeWindow {
this._win = new BrowserWindow(options); this._win = new BrowserWindow(options);
this._id = this._win.id; 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) { if (isFullscreenOrMaximized) {
this.win.maximize(); this.win.maximize();
@@ -238,9 +224,28 @@ export class VSCodeWindow implements IVSCodeWindow {
this.setMenuBarVisibility(false); // respect configured menu bar visibility 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(); 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 { public hasHiddenTitleBarStyle(): boolean {
return this.hiddenTitleBarStyle; return this.hiddenTitleBarStyle;
} }
@@ -478,11 +483,12 @@ export class VSCodeWindow implements IVSCodeWindow {
windowConfiguration.fullscreen = this._win.isFullScreen(); windowConfiguration.fullscreen = this._win.isFullScreen();
// Set Accessibility Config // Set Accessibility Config
windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme(); windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme() && (!windowConfig || windowConfig.autoDetectHighContrast);
windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled(); windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled();
// Perf Counters // Perf Counters
windowConfiguration.perfStartTime = global.perfStartTime; windowConfiguration.perfStartTime = global.perfStartTime;
windowConfiguration.perfAppReady = global.perfAppReady;
windowConfiguration.perfWindowLoadTime = Date.now(); windowConfiguration.perfWindowLoadTime = Date.now();
// Config (combination of process.argv and window configuration) // Config (combination of process.argv and window configuration)
+61 -28
View File
@@ -34,12 +34,32 @@ enum WindowError {
CRASHED 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 { export interface IOpenConfiguration {
context: OpenContext;
cli: ParsedArgs; cli: ParsedArgs;
userEnv?: platform.IProcessEnvironment; userEnv?: platform.IProcessEnvironment;
pathsToOpen?: string[]; pathsToOpen?: string[];
preferNewWindow?: boolean; preferNewWindow?: boolean;
forceNewWindow?: boolean; forceNewWindow?: boolean;
forceReuseWindow?: boolean;
forceEmpty?: boolean; forceEmpty?: boolean;
windowToUse?: VSCodeWindow; windowToUse?: VSCodeWindow;
diffMode?: boolean; diffMode?: boolean;
@@ -96,10 +116,10 @@ export interface IWindowsMainService {
openFilePicker(forceNewWindow?: boolean, path?: string, window?: VSCodeWindow): void; openFilePicker(forceNewWindow?: boolean, path?: string, window?: VSCodeWindow): void;
openFolderPicker(forceNewWindow?: boolean, window?: VSCodeWindow): void; openFolderPicker(forceNewWindow?: boolean, window?: VSCodeWindow): void;
openAccessibilityOptions(): void; openAccessibilityOptions(): void;
focusLastActive(cli: ParsedArgs): VSCodeWindow; focusLastActive(cli: ParsedArgs, context: OpenContext): VSCodeWindow;
getLastActiveWindow(): VSCodeWindow; getLastActiveWindow(): VSCodeWindow;
findWindow(workspacePath: string, filePath?: string, extensionDevelopmentPath?: string): VSCodeWindow; findWindow(workspacePath: string, filePath?: string, extensionDevelopmentPath?: string): VSCodeWindow;
openNewWindow(): void; openNewWindow(context: OpenContext): void;
sendToFocused(channel: string, ...args: any[]): void; sendToFocused(channel: string, ...args: any[]): void;
sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void; sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void;
getFocusedWindow(): VSCodeWindow; getFocusedWindow(): VSCodeWindow;
@@ -166,7 +186,7 @@ export class WindowsManager implements IWindowsMainService {
// Mac only event: open new window when we get activated // Mac only event: open new window when we get activated
if (!hasVisibleWindows) { 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! // Handle paths delayed in case more are coming!
runningTimeout = setTimeout(() => { 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 = []; macOpenFiles = [];
runningTimeout = null; runningTimeout = null;
}, 100); }, 100);
@@ -272,6 +297,8 @@ export class WindowsManager implements IWindowsMainService {
} }
public open(openConfig: IOpenConfiguration): VSCodeWindow[] { public open(openConfig: IOpenConfiguration): VSCodeWindow[] {
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
let iPathsToOpen: IPath[]; let iPathsToOpen: IPath[];
const usedWindows: VSCodeWindow[] = []; const usedWindows: VSCodeWindow[] = [];
@@ -345,22 +372,26 @@ export class WindowsManager implements IWindowsMainService {
filesToOpen = candidates; 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 // 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)) { 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; let openFilesInNewWindow: boolean;
if (openConfig.forceNewWindow) { if (openConfig.forceNewWindow || openConfig.forceReuseWindow) {
openFilesInNewWindow = true; openFilesInNewWindow = openConfig.forceNewWindow && !openConfig.forceReuseWindow;
} else { } else {
openFilesInNewWindow = openConfig.preferNewWindow; if (openConfig.context === OpenContext.DOCK) {
if (openFilesInNewWindow && !openConfig.cli.extensionDevelopmentPath) { // can be overriden via settings (not for PDE though!) openFilesInNewWindow = true; // only on macOS do we allow to open files in a new window if this is triggered via DOCK context
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.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(); const lastActiveWindow = this.getLastActiveWindow();
if (!openFilesInNewWindow && lastActiveWindow) { if (!openFilesInNewWindow && lastActiveWindow) {
lastActiveWindow.focus(); lastActiveWindow.focus();
const files = { filesToOpen, filesToCreate, filesToDiff }; // copy to object because they get reset shortly after
lastActiveWindow.ready().then(readyWindow => { lastActiveWindow.ready().then(readyWindow => {
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff }); readyWindow.send('vscode:openFiles', files);
}); });
usedWindows.push(lastActiveWindow); usedWindows.push(lastActiveWindow);
@@ -381,7 +413,7 @@ export class WindowsManager implements IWindowsMainService {
const browserWindow = this.openInBrowserWindow(configuration, true /* new window */); const browserWindow = this.openInBrowserWindow(configuration, true /* new window */);
usedWindows.push(browserWindow); 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 // Reset these because we handled them
@@ -399,8 +431,9 @@ export class WindowsManager implements IWindowsMainService {
if (windowsOnWorkspacePath.length > 0) { if (windowsOnWorkspacePath.length > 0) {
const browserWindow = windowsOnWorkspacePath[0]; const browserWindow = windowsOnWorkspacePath[0];
browserWindow.focus(); // just focus one of them 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 => { browserWindow.ready().then(readyWindow => {
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff }); readyWindow.send('vscode:openFiles', files);
}); });
usedWindows.push(browserWindow); usedWindows.push(browserWindow);
@@ -410,7 +443,7 @@ export class WindowsManager implements IWindowsMainService {
filesToCreate = []; filesToCreate = [];
filesToDiff = []; 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 // Open remaining ones
@@ -420,7 +453,7 @@ export class WindowsManager implements IWindowsMainService {
} }
const configuration = this.toConfiguration(openConfig, folderToOpen, filesToOpen, filesToCreate, filesToDiff); 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); usedWindows.push(browserWindow);
// Reset these because we handled them // Reset these because we handled them
@@ -428,7 +461,7 @@ export class WindowsManager implements IWindowsMainService {
filesToCreate = []; filesToCreate = [];
filesToDiff = []; 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); const browserWindow = this.openInBrowserWindow(configuration, true /* new window */, null, emptyWorkspaceBackupFolder);
usedWindows.push(browserWindow); 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) { else if (emptyToOpen.length > 0) {
emptyToOpen.forEach(() => { emptyToOpen.forEach(() => {
const configuration = this.toConfiguration(openConfig); 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); 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 // 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 { 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 { private doPickAndOpen(options: INativeOpenDialogOptions): void {
this.getFileOrFolderPaths(options, (paths: string[]) => { this.getFileOrFolderPaths(options, (paths: string[]) => {
if (paths && paths.length) { 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(); const lastActive = this.getLastActiveWindow();
if (lastActive) { if (lastActive) {
lastActive.focus(); lastActive.focus();
@@ -932,7 +965,7 @@ export class WindowsManager implements IWindowsMainService {
// No window - open new one // No window - open new one
this.windowsState.openedFolders = []; // make sure we do not open too much 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]; return res && res[0];
} }
@@ -994,8 +1027,8 @@ export class WindowsManager implements IWindowsMainService {
return null; return null;
} }
public openNewWindow(): void { public openNewWindow(context: OpenContext): void {
this.open({ cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true }); this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
} }
public sendToFocused(channel: string, ...args: any[]): void { 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 { class CSSBasedConfiguration extends Disposable {
public static INSTANCE = new CSSBasedConfiguration(); 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 { private static _actualReadConfiguration(bareFontInfo: BareFontInfo): FontInfo {
let typicalHalfwidthCharacter = new CharWidthReader('n'); let canvasElem = <HTMLCanvasElement>document.createElement('canvas');
let typicalFullwidthCharacter = new CharWidthReader('\uff4d'); let context = canvasElem.getContext('2d');
let space = new CharWidthReader(' '); context.font = `normal normal normal normal ${bareFontInfo.fontSize}px / ${bareFontInfo.lineHeight}px ${bareFontInfo.fontFamily}`;
let digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].map(chr => new CharWidthReader(chr));
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; let maxDigitWidth = 0;
for (let i = 0, len = digits.length; i < len; i++) { 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 { onUnexpectedError } from 'vs/base/common/errors';
import { EmitterEvent, IEventEmitter } from 'vs/base/common/eventEmitter'; import { EmitterEvent, IEventEmitter } from 'vs/base/common/eventEmitter';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; 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 browser from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import { StyleMutator } from 'vs/base/browser/styleMutator'; 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)) { if (!dom.isInDOM(this.domNode)) {
return; return;
} }
let t = timer.start(timer.Topic.EDITOR, 'View.render');
let viewPartsToRender = this._getViewPartsToRender(); let viewPartsToRender = this._getViewPartsToRender();
if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) { if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) {
// Nothing to render // Nothing to render
this.keyboardHandler.writeToTextArea(); this.keyboardHandler.writeToTextArea();
t.stop();
return; return;
} }
@@ -940,8 +937,6 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
// Render the scrollbar // Render the scrollbar
this.layoutProvider.renderScrollbar(); this.layoutProvider.renderScrollbar();
t.stop();
} }
private _setHasFocus(newHasFocus: boolean): void { 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; protected abstract readConfiguration(styling: editorCommon.BareFontInfo): editorCommon.FontInfo;
} }
/** const configurationRegistry = <IConfigurationRegistry>Registry.as(Extensions.Configuration);
* Helper to update Monaco Editor Settings from configurations service. const editorConfiguration: IConfigurationNode = {
*/
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 = {
'id': 'editor', 'id': 'editor',
'order': 5, 'order': 5,
'type': 'object', 'type': 'object',
@@ -8,7 +8,6 @@ import * as nls from 'vs/nls';
import { onUnexpectedError } from 'vs/base/common/errors'; import { onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { StopWatch } from 'vs/base/common/stopwatch'; import { StopWatch } from 'vs/base/common/stopwatch';
import * as timer from 'vs/base/common/timer';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon'; import * as editorCommon from 'vs/editor/common/editorCommon';
import { TextModel } from 'vs/editor/common/model/textModel'; import { TextModel } from 'vs/editor/common/model/textModel';
@@ -294,7 +293,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
this._withModelTokensChangedEventBuilder((eventBuilder) => { this._withModelTokensChangedEventBuilder((eventBuilder) => {
var t1 = timer.start(timer.Topic.EDITOR, 'backgroundTokenization');
toLineNumber = Math.min(this._lines.length, toLineNumber); toLineNumber = Math.min(this._lines.length, toLineNumber);
var MAX_ALLOWED_TIME = 20, var MAX_ALLOWED_TIME = 20,
@@ -342,8 +340,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
if (this._invalidLineStartIndex < this._lines.length) { if (this._invalidLineStartIndex < this._lines.length) {
this._beginBackgroundTokenization(); this._beginBackgroundTokenization();
} }
t1.stop();
}); });
} }
+1 -1
View File
@@ -125,7 +125,7 @@ const enum CharacterClass {
} }
const classifier = (function () { const classifier = (function () {
let result = new CharacterClassifier(CharacterClass.None); let result = new CharacterClassifier<CharacterClass>(CharacterClass.None);
const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…'; const FORCE_TERMINATION_CHARACTERS = ' \t<>\'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…';
for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) { for (let i = 0; i < FORCE_TERMINATION_CHARACTERS.length; i++) {
+9 -6
View File
@@ -34,7 +34,7 @@ class ChangeRecorder {
private _fileService: IFileService; private _fileService: IFileService;
constructor(fileService: IFileService) { constructor(fileService?: IFileService) {
this._fileService = fileService; this._fileService = fileService;
} }
@@ -42,7 +42,9 @@ class ChangeRecorder {
const changes: IStringDictionary<IFileChange[]> = Object.create(null); 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 => { event.changes.forEach(change => {
const key = String(change.resource); const key = String(change.resource);
@@ -55,9 +57,10 @@ class ChangeRecorder {
array.push(change); array.push(change);
}); });
}); });
}
return { return {
stop: () => { stop.dispose(); }, stop: () => { return stop && stop.dispose(); },
hasChanged: (resource: URI) => !!changes[resource.toString()], hasChanged: (resource: URI) => !!changes[resource.toString()],
allChanges: () => flatten(values(changes)) allChanges: () => flatten(values(changes))
}; };
@@ -273,14 +276,14 @@ export interface BulkEdit {
finish(): TPromise<ISelection>; finish(): TPromise<ISelection>;
} }
export function bulkEdit(fileService: IFileService, textModelResolverService: ITextModelResolverService, editor: ICommonCodeEditor, edits: IResourceEdit[], progress: IProgressRunner = null): TPromise<any> { export function bulkEdit(textModelResolverService: ITextModelResolverService, editor: ICommonCodeEditor, edits: IResourceEdit[], fileService?: IFileService, progress: IProgressRunner = null): TPromise<any> {
let bulk = createBulkEdit(fileService, textModelResolverService, editor); let bulk = createBulkEdit(textModelResolverService, editor, fileService);
bulk.add(edits); bulk.add(edits);
bulk.progress(progress); bulk.progress(progress);
return bulk.finish(); 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 all: IResourceEdit[] = [];
let recording = new ChangeRecorder(fileService).start(); 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 { TPromise } from 'vs/base/common/winjs.base';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; 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 { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; 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 { ICommonCodeEditor, IEditorContribution, MouseTargetType, EditorContextKeys, IScrollEvent } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
@@ -125,9 +124,17 @@ export class ContextMenuController implements IEditorContribution {
private _getMenuActions(): IAction[] { private _getMenuActions(): IAction[] {
const result: 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(); contextMenu.dispose();
for (let group of groups) {
const [, actions] = group;
result.push(...actions);
result.push(new Separator());
}
result.pop(); // remove last separator
return result; return result;
} }
@@ -105,10 +105,12 @@
} }
.monaco-editor .find-widget.no-results .matchesCount { .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 { .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_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_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_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; let MAX_MATCHES_COUNT_WIDTH = 69;
const WIDGET_FIXED_WIDTH = 411 - 69; const WIDGET_FIXED_WIDTH = 411 - 69;
@@ -76,7 +76,7 @@ export class DefinitionAction extends EditorAction {
let result: Location[] = []; let result: Location[] = [];
for (let i = 0; i < references.length; i++) { for (let i = 0; i < references.length; i++) {
let reference = references[i]; let reference = references[i];
if (!reference) { if (!reference || !reference.range) {
continue; continue;
} }
let {uri, range} = reference; let {uri, range} = reference;
@@ -110,7 +110,7 @@ export class DefinitionAction extends EditorAction {
} else { } else {
let next = model.nearestReference(editor.getModel().uri, editor.getPosition()); let next = model.nearestReference(editor.getModel().uri, editor.getPosition());
this._openReference(editorService, next, this._configuration.openToSide).then(editor => { 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); this._openInPeek(editorService, editor, model);
} else { } else {
model.dispose(); model.dispose();
@@ -128,7 +128,7 @@ export class DefinitionAction extends EditorAction {
revealIfVisible: !sideBySide revealIfVisible: !sideBySide
} }
}, sideBySide).then(editor => { }, sideBySide).then(editor => {
return <editorCommon.IEditor>editor.getControl(); return editor && <editorCommon.IEditor>editor.getControl();
}); });
} }
@@ -8,6 +8,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 16px;
width: 16px;
} }
.monaco-editor .lightbulb-glyph:hover { .monaco-editor .lightbulb-glyph:hover {
@@ -16,12 +18,18 @@
.monaco-editor.vs .lightbulb-glyph { .monaco-editor.vs .lightbulb-glyph {
background: url('lightbulb.svg') center center no-repeat; background: url('lightbulb.svg') center center no-repeat;
height: 16px;
width: 16px;
} }
.monaco-editor.vs-dark .lightbulb-glyph, .monaco-editor.hc-black .lightbulb-glyph { .monaco-editor.vs .lightbulb-glyph[data-severity="high"]{
background: url('lightbulb-dark.svg') center center no-repeat; 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;
}
.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 'vs/css!./lightBulbWidget';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { Emitter, any } from 'vs/base/common/event'; 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 * as dom from 'vs/base/browser/dom';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; 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 { export class LightBulbWidget implements IOverlayWidget, IDisposable {
@@ -86,7 +87,7 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable {
const modelNow = this._model; const modelNow = this._model;
e.fixes.done(fixes => { e.fixes.done(fixes => {
if (modelNow === this._model && fixes && fixes.length > 0) { if (modelNow === this._model && fixes && fixes.length > 0) {
this.show(e.range.startLineNumber); this.show(e);
} else { } else {
this.hide(); this.hide();
} }
@@ -99,7 +100,8 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable {
return this._model; return this._model;
} }
show(line: number): void { show(e: QuickFixComputeEvent): void {
const line = e.range.startLineNumber;
if (!this._hasSpaceInGlyphMargin(line)) { if (!this._hasSpaceInGlyphMargin(line)) {
return; return;
} }
@@ -107,6 +109,7 @@ export class LightBulbWidget implements IOverlayWidget, IDisposable {
this._line = line; this._line = line;
this._visible = true; this._visible = true;
this._layout(); 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 { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { QuickFixContextMenu } from './quickFixWidget'; import { QuickFixContextMenu } from 'vs/editor/contrib/quickFix/browser/quickFixWidget';
import { LightBulbWidget } from './lightBulbWidget'; import { LightBulbWidget } from 'vs/editor/contrib/quickFix/browser/lightBulbWidget';
import { QuickFixModel, QuickFixComputeEvent } from './quickFixModel'; import { QuickFixModel, QuickFixComputeEvent } from 'vs/editor/contrib/quickFix/common/quickFixModel';
@editorContribution @editorContribution
export class QuickFixController implements IEditorContribution { export class QuickFixController implements IEditorContribution {
@@ -88,7 +88,7 @@ export class QuickFixController implements IEditorContribution {
} }
public triggerFromEditorSelection(): void { public triggerFromEditorSelection(): void {
this._model.triggerManual(this._editor.getSelection()); this._model.triggerManual();
} }
private _updateLightBulbTitle(): void { private _updateLightBulbTitle(): void {
@@ -6,21 +6,21 @@
import * as arrays from 'vs/base/common/arrays'; import * as arrays from 'vs/base/common/arrays';
import Event, { Emitter } from 'vs/base/common/event'; import Event, { Emitter } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri'; import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers'; import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers';
import { Range } from 'vs/editor/common/core/range'; 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 { ICommonCodeEditor, IPosition, IRange } from 'vs/editor/common/editorCommon';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CodeActionProviderRegistry, CodeAction } from 'vs/editor/common/modes'; import { CodeActionProviderRegistry, CodeAction } from 'vs/editor/common/modes';
import { getCodeActions } from '../common/quickFix'; import { getCodeActions } from '../common/quickFix';
class QuickFixOracle { export class QuickFixOracle {
private _disposables: IDisposable[] = []; private _disposables: IDisposable[] = [];
private _currentRange: IRange;
constructor(private _editor: ICommonCodeEditor, private _markerService: IMarkerService, private _signalChange: (e: QuickFixComputeEvent) => any) { constructor(private _editor: ICommonCodeEditor, private _markerService: IMarkerService, private _signalChange: (e: QuickFixComputeEvent) => any) {
@@ -34,29 +34,57 @@ class QuickFixOracle {
this._disposables = dispose(this._disposables); this._disposables = dispose(this._disposables);
} }
private _onMarkerChanges(resources: URI[]): void { trigger(): void {
const {uri} = this._editor.getModel(); let {range, severity} = this._rangeAtPosition();
let affectedBy = false; if (!range) {
for (const resource of resources) { range = this._editor.getSelection();
if (resource.toString() === uri.toString()) {
affectedBy = true;
break;
} }
}
if (affectedBy) {
this._onCursorChange();
}
}
private _onCursorChange(): void {
const range = this._markerAtPosition() || this._wordAtPosition();
this._signalChange({ this._signalChange({
type: 'auto', type: 'manual',
severity,
range, range,
position: this._editor.getPosition(), position: this._editor.getPosition(),
fixes: range && getCodeActions(this._editor.getModel(), this._editor.getModel().validateRange(range)) 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 { private _markerAtPosition(): IMarker {
@@ -76,26 +104,23 @@ class QuickFixOracle {
} }
private _wordAtPosition(): IRange { private _wordAtPosition(): IRange {
return; const {positionLineNumber, positionColumn} = this._editor.getSelection();
// todo@joh - enable once we decide to eagerly show the const model = this._editor.getModel();
// light bulb as the cursor moves const info = model.getWordAtPosition({ lineNumber: positionLineNumber, column: positionColumn });
// const {positionLineNumber, positionColumn} = this._editor.getSelection(); if (info) {
// const model = this._editor.getModel(); return {
startLineNumber: positionLineNumber,
// const info = model.getWordAtPosition({ lineNumber: positionLineNumber, column: positionColumn }); startColumn: info.startColumn,
// if (info) { endLineNumber: positionLineNumber,
// return { endColumn: info.endColumn
// startLineNumber: positionLineNumber, };
// startColumn: info.startColumn, }
// endLineNumber: positionLineNumber,
// endColumn: info.endColumn
// };
// }
} }
} }
export interface QuickFixComputeEvent { export interface QuickFixComputeEvent {
type: 'auto' | 'manual'; type: 'auto' | 'manual';
severity: Severity;
range: IRange; range: IRange;
position: IPosition; position: IPosition;
fixes: TPromise<CodeAction[]>; fixes: TPromise<CodeAction[]>;
@@ -109,7 +134,7 @@ export class QuickFixModel {
private _onDidChangeFixes = new Emitter<QuickFixComputeEvent>(); private _onDidChangeFixes = new Emitter<QuickFixComputeEvent>();
private _disposables: IDisposable[] = []; private _disposables: IDisposable[] = [];
constructor(editor: ICodeEditor, markerService: IMarkerService) { constructor(editor: ICommonCodeEditor, markerService: IMarkerService) {
this._editor = editor; this._editor = editor;
this._markerService = markerService; this._markerService = markerService;
@@ -132,7 +157,8 @@ export class QuickFixModel {
private _update(): void { private _update(): void {
if (this._quickFixOracle) { if (this._quickFixOracle) {
dispose(this._quickFixOracle); this._quickFixOracle.dispose();
this._quickFixOracle = undefined;
this._onDidChangeFixes.fire(undefined); this._onDidChangeFixes.fire(undefined);
} }
@@ -144,16 +170,9 @@ export class QuickFixModel {
} }
} }
triggerManual(selection: Selection): void { triggerManual(): void {
const model = this._editor.getModel(); if (this._quickFixOracle) {
if (model) { this._quickFixOracle.trigger();
const fixes = getCodeActions(model, selection);
this._onDidChangeFixes.fire({
type: 'manual',
range: selection,
position: { lineNumber: selection.positionLineNumber, column: selection.positionColumn },
fixes
});
} }
} }
} }
@@ -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 { rename } from '../common/rename';
import RenameInputField from './renameInputField'; import RenameInputField from './renameInputField';
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
import { optional } from 'vs/platform/instantiation/common/instantiation';
// --- register actions and commands // --- register actions and commands
@@ -42,10 +43,10 @@ class RenameController implements IEditorContribution {
constructor( constructor(
private editor: ICodeEditor, private editor: ICodeEditor,
@IMessageService private _messageService: IMessageService, @IMessageService private _messageService: IMessageService,
@IFileService private _fileService: IFileService,
@ITextModelResolverService private _textModelResolverService: ITextModelResolverService, @ITextModelResolverService private _textModelResolverService: ITextModelResolverService,
@IProgressService private _progressService: IProgressService, @IProgressService private _progressService: IProgressService,
@IContextKeyService contextKeyService: IContextKeyService @IContextKeyService contextKeyService: IContextKeyService,
@optional(IFileService) private _fileService: IFileService
) { ) {
this._renameInputField = new RenameInputField(editor); this._renameInputField = new RenameInputField(editor);
this._renameInputVisible = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); 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 // start recording of file changes so that we can figure out if a file that
// is to be renamed conflicts with another (concurrent) modification // 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 => { return rename(this.editor.getModel(), this.editor.getPosition(), newName).then(result => {
if (result.rejectReason) { if (result.rejectReason) {
@@ -122,15 +122,15 @@ export class CodeSnippet implements ICodeSnippet {
for (let {startLineNumber, startColumn, endLineNumber, endColumn} of originalPlaceHolder.occurences) { for (let {startLineNumber, startColumn, endLineNumber, endColumn} of originalPlaceHolder.occurences) {
if (startColumn > 1) { if (startColumn > 1 || startLineNumber === 1) {
// placeholders that aren't at the beginning of the snippet line // placeholders that aren't at the beginning of new snippet lines
// will be moved by how many characters the indentation has been // will be moved by how many characters the indentation has been
// adjusted // adjusted
startColumn = startColumn + deltaColumns[startLineNumber]; startColumn = startColumn + deltaColumns[startLineNumber];
endColumn = endColumn + deltaColumns[endLineNumber]; endColumn = endColumn + deltaColumns[endLineNumber];
} else { } else {
// placeholders at the beginning of the snippet line // placeholders at the beginning of new snippet lines
// will be indented by the reference indentation // will be indented by the reference indentation
startColumn += referenceIndentation.length; startColumn += referenceIndentation.length;
endColumn += referenceIndentation.length; endColumn += referenceIndentation.length;
@@ -140,7 +140,7 @@ export class CodeSnippet implements ICodeSnippet {
startLineNumber: startLineNumber + deltaLine, startLineNumber: startLineNumber + deltaLine,
startColumn, startColumn,
endLineNumber: endLineNumber + deltaLine, 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([ let snippet = CodeSnippet.fromTextmate([
'afterEach((done) => {', 'afterEach((done) => {',
@@ -242,6 +242,7 @@ suite('Editor Contrib - Snippets', () => {
assert.equal(boundSnippet.lines[1], ' test'); assert.equal(boundSnippet.lines[1], ' test');
assert.equal(boundSnippet.placeHolders.length, 3); assert.equal(boundSnippet.placeHolders.length, 3);
assert.equal(boundSnippet.finishPlaceHolderIndex, 2); assert.equal(boundSnippet.finishPlaceHolderIndex, 2);
let [first, second] = boundSnippet.placeHolders; let [first, second] = boundSnippet.placeHolders;
assert.equal(first.occurences.length, 1); assert.equal(first.occurences.length, 1);
assert.equal(first.occurences[0].startColumn, 1); assert.equal(first.occurences[0].startColumn, 1);
@@ -249,6 +250,49 @@ suite('Editor Contrib - Snippets', () => {
assert.equal(second.occurences[0].startColumn, 7); 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', () => { test('variables, simple', () => {
const resolver: ISnippetVariableResolver = { const resolver: ISnippetVariableResolver = {
@@ -9,9 +9,8 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { forEach } from 'vs/base/common/collections'; import { forEach } from 'vs/base/common/collections';
import Event, { Emitter } from 'vs/base/common/event'; import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { startsWith } from 'vs/base/common/strings';
import { TPromise } from 'vs/base/common/winjs.base'; 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 { ISuggestSupport, SuggestRegistry } from 'vs/editor/common/modes';
import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest'; import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest';
import { CompletionModel } from './completionModel'; import { CompletionModel } from './completionModel';
@@ -30,104 +29,51 @@ export interface ISuggestEvent {
auto: boolean; 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 lineNumber: number;
readonly column: number; readonly column: number;
readonly isInEditableRange: boolean; readonly leadingLineContent: string;
readonly leadingWord: IWordAtPosition;
readonly lineContentBefore: string; readonly auto;
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 = '';
}
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.lineNumber = position.lineNumber;
this.column = position.column; this.column = position.column;
this.lineContentBefore = lineContent.substr(0, position.column - 1); this.auto = auto;
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;
} }
} }
@@ -147,7 +93,7 @@ export class SuggestModel implements IDisposable {
private state: State; private state: State;
private requestPromise: TPromise<void>; private requestPromise: TPromise<void>;
private context: Context; private context: LineContext;
private completionModel: CompletionModel; private completionModel: CompletionModel;
@@ -274,12 +220,11 @@ export class SuggestModel implements IDisposable {
} }
private onCursorChange(e: ICursorSelectionChangedEvent): void { 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(); this.cancel();
return; return;
} }
@@ -288,32 +233,28 @@ export class SuggestModel implements IDisposable {
return; return;
} }
const isInactive = this.state === State.Idle;
if (isInactive && !this.editor.getConfiguration().contribInfo.quickSuggestions) {
return;
}
const model = this.editor.getModel(); const model = this.editor.getModel();
if (!model) { if (!model) {
return; return;
} }
const ctx = new Context(model, this.editor.getPosition(), false); if (this.state === State.Idle) {
if (isInactive) { if (this.editor.getConfiguration().contribInfo.quickSuggestions) {
// trigger was not called or it was canceled // trigger 24x7 IntelliSense when idle and enabled
this.cancel(); this.cancel();
if (LineContext.shouldAutoTrigger(this.editor)) {
if (ctx.shouldAutoTrigger()) {
this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay); this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay);
this.triggerAutoSuggestPromise.then(() => { this.triggerAutoSuggestPromise.then(() => {
this.triggerAutoSuggestPromise = null; this.triggerAutoSuggestPromise = null;
this.trigger(true); this.trigger(true);
}); });
} }
}
} else { } else {
// refine active suggestion
const ctx = new LineContext(model, this.editor.getPosition(), this.state === State.Auto);
this.onNewContext(ctx); this.onNewContext(ctx);
} }
} }
@@ -326,9 +267,9 @@ export class SuggestModel implements IDisposable {
return; 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; return;
} }
@@ -359,9 +300,9 @@ export class SuggestModel implements IDisposable {
items = items.concat(existingItems).sort(cmpFn); 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, { this.completionModel = new CompletionModel(items, this.context.column, {
leadingLineContent: ctx.lineContentBefore, leadingLineContent: ctx.leadingLineContent,
characterCountDelta: this.context ? ctx.column - this.context.column : 0 characterCountDelta: this.context ? ctx.column - this.context.column : 0
}); });
this.onNewContext(ctx); this.onNewContext(ctx);
@@ -369,42 +310,66 @@ export class SuggestModel implements IDisposable {
}).then(null, onUnexpectedError); }).then(null, onUnexpectedError);
} }
private onNewContext(ctx: Context): void { private onNewContext(ctx: LineContext): void {
if (this.context && this.context.isDifferentContext(ctx)) {
if (this.context.shouldRetrigger(ctx)) {
this.trigger(this.state === State.Auto, true);
} else {
this.cancel();
}
} else if (this.completionModel) { if (!this.context) {
// happens when 24x7 IntelliSense is enabled and still in its delay
if (this.completionModel.incomplete && ctx.column > this.context.column) {
const {complete, incomplete} = this.completionModel.resolveIncompleteInfo();
this.trigger(this.state === State.Auto, true, incomplete, complete);
return; return;
} }
const auto = this.state === State.Auto; if (ctx.lineNumber !== this.context.lineNumber) {
const oldLineContext = this.completionModel.lineContext; // 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; let isFrozen = false;
this.completionModel.lineContext = { this.completionModel.lineContext = {
leadingLineContent: ctx.lineContentBefore, leadingLineContent: ctx.leadingLineContent,
characterCountDelta: this.context ? ctx.column - this.context.column : 0 characterCountDelta: ctx.column - this.context.column
}; };
// when explicitly request when the next context goes if (this.completionModel.items.length === 0) {
// from 'results' to 'no results' freeze
if (!auto && 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; this.completionModel.lineContext = oldLineContext;
isFrozen = this.completionModel.items.length > 0; isFrozen = this.completionModel.items.length > 0;
} }
}
this._onDidSuggest.fire({ this._onDidSuggest.fire({
completionModel: this.completionModel, completionModel: this.completionModel,
auto: this.context.auto,
isFrozen, isFrozen,
auto
}); });
} }
} }
@@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { Model } from 'vs/editor/common/model/model'; import { Model } from 'vs/editor/common/model/model';
import { ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon'; import { ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon';
import { ISuggestSupport, ISuggestResult, SuggestRegistry } from 'vs/editor/common/modes'; 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 { MockCodeEditor, MockScopeLocation } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
@@ -49,8 +49,10 @@ suite('SuggestModel - Context', function () {
function assertAutoTrigger(offset: number, expected: boolean): void { function assertAutoTrigger(offset: number, expected: boolean): void {
const pos = model.getPositionAt(offset); const pos = model.getPositionAt(offset);
const ctx = new Context(model, pos, false); const editor = createMockEditor(model);
assert.equal(ctx.shouldAutoTrigger(), expected); editor.setPosition(pos);
assert.equal(LineContext.shouldAutoTrigger(editor), expected);
editor.dispose();
} }
assertAutoTrigger(3, true); // end of word, Das| assertAutoTrigger(3, true); // end of word, Das|
@@ -59,28 +61,6 @@ suite('SuggestModel - Context', function () {
assertAutoTrigger(55, false); // number, 1861| 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 () { 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'; 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); const groups = menu.getActions(context);
if (groups.length === 0) { if (groups.length === 0) {
return; return;
@@ -25,11 +25,6 @@ export function fillInActions(menu: IMenu, context: any, target: IAction[] | { p
for (let tuple of groups) { for (let tuple of groups) {
let [group, actions] = tuple; let [group, actions] = tuple;
if (!ignoreAltKey && _altKey.value) {
swapWithAltActionsIfPossible(actions);
}
if (group === 'navigation') { if (group === 'navigation') {
const head = Array.isArray<IAction>(target) ? target : target.primary; 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 { export function createActionItem(action: IAction, keybindingService: IKeybindingService, messageService: IMessageService): ActionItem {
if (action instanceof MenuItemAction) { if (action instanceof MenuItemAction) {
@@ -86,8 +74,6 @@ const _altKey = new class extends Emitter<boolean> {
private _subscriptions: IDisposable[] = []; private _subscriptions: IDisposable[] = [];
private _value = false;
constructor() { constructor() {
super(); super();
@@ -97,15 +83,6 @@ const _altKey = new class extends Emitter<boolean> {
this._subscriptions.push(domEvent(document.body, 'blur')(e => this.fire(false))); 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() { dispose() {
super.dispose(); super.dispose();
this._subscriptions = dispose(this._subscriptions); this._subscriptions = dispose(this._subscriptions);
@@ -82,7 +82,7 @@ export function getConfigurationValue<T>(config: any, settingPath: string, defau
let current = config; let current = config;
for (let i = 0; i < path.length; i++) { for (let i = 0; i < path.length; i++) {
current = current[path[i]]; current = current[path[i]];
if (typeof current === 'undefined') { if (typeof current === 'undefined' || current === null) {
return undefined; 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 { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import Event from 'vs/base/common/event'; import Event from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
export const IEditorService = createDecorator<IEditorService>('editorService'); export const IEditorService = createDecorator<IEditorService>('editorService');
@@ -238,6 +239,11 @@ export interface ITextEditorOptions extends IEditorOptions {
endColumn?: number; 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. * 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; diff?: boolean;
goto?: boolean; goto?: boolean;
'new-window'?: boolean; 'new-window'?: boolean;
'new-window-if-not-first'?: boolean;
'reuse-window'?: boolean; 'reuse-window'?: boolean;
locale?: string; locale?: string;
'user-data-dir'?: string; 'user-data-dir'?: string;
+2 -1
View File
@@ -30,6 +30,7 @@ const options: minimist.Opts = {
'diff', 'diff',
'goto', 'goto',
'new-window', 'new-window',
'new-window-if-not-first',
'reuse-window', 'reuse-window',
'performance', 'performance',
'verbose', 'verbose',
@@ -148,7 +149,7 @@ export function formatOptions(options: { [name: string]: string; }, columns: num
} }
function wrapText(text: string, columns: number): string[] { function wrapText(text: string, columns: number): string[] {
let lines = []; let lines: string[] = [];
while (text.length) { while (text.length) {
let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns); let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns);
let line = text.slice(0, index).trim(); let line = text.slice(0, index).trim();
+3 -3
View File
@@ -223,7 +223,7 @@ export class FileChangesEvent extends events.Event {
return false; return false;
} }
return this._changes.some((change) => { return this._changes.some(change => {
if (change.type !== type) { if (change.type !== type) {
return false; return false;
} }
@@ -280,11 +280,11 @@ export class FileChangesEvent extends events.Event {
} }
private getOfType(type: FileChangeType): IFileChange[] { 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 { private hasType(type: FileChangeType): boolean {
return this._changes.some((change) => { return this._changes.some(change => {
return change.type === type; return change.type === type;
}); });
} }
@@ -123,13 +123,15 @@ export class InstantiationService implements IInstantiationService {
// arguments defined by service decorators // arguments defined by service decorators
let serviceDependencies = _util.getServiceDependencies(desc.ctor).sort((a, b) => a.index - b.index); 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); let service = this._getOrCreateServiceInstance(dependency.id);
if (!service && this._strict && !dependency.optional) { if (!service && this._strict && !dependency.optional) {
throw new Error(`[createInstance] ${desc.ctor.name} depends on UNKNOWN service ${dependency.id}.`); 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; let firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : staticArgs.length;
// check for argument mismatches, adjust static args if needed // check for argument mismatches, adjust static args if needed
@@ -23,7 +23,7 @@ export interface ICommandAndKeybindingRule extends IKeybindingRule {
} }
export interface IKeybindingsRegistry { export interface IKeybindingsRegistry {
registerKeybindingRule(rule: IKeybindingRule); registerKeybindingRule(rule: IKeybindingRule): void;
registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): void; registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): void;
getDefaultKeybindings(): IKeybindingItem[]; getDefaultKeybindings(): IKeybindingItem[];
+4 -3
View File
@@ -25,6 +25,7 @@ export interface IWindowsService {
openDevTools(windowId: number): TPromise<void>; openDevTools(windowId: number): TPromise<void>;
toggleDevTools(windowId: number): TPromise<void>; toggleDevTools(windowId: number): TPromise<void>;
// TODO@joao: rename, shouldn't this be closeWindow? // 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>; closeFolder(windowId: number): TPromise<void>;
toggleFullScreen(windowId: number): TPromise<void>; toggleFullScreen(windowId: number): TPromise<void>;
setRepresentedFilename(windowId: number, fileName: string): TPromise<void>; setRepresentedFilename(windowId: number, fileName: string): TPromise<void>;
@@ -40,8 +41,7 @@ export interface IWindowsService {
quit(): TPromise<void>; quit(): TPromise<void>;
// Global methods // Global methods
// TODO@joao: rename, shouldn't this be openWindow? openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void>;
windowOpen(paths: string[], forceNewWindow?: boolean): TPromise<void>;
openNewWindow(): TPromise<void>; openNewWindow(): TPromise<void>;
showWindow(windowId: number): TPromise<void>; showWindow(windowId: number): TPromise<void>;
getWindows(): TPromise<{ id: number; path: string; title: string; }[]>; getWindows(): TPromise<{ id: number; path: string; title: string; }[]>;
@@ -87,7 +87,8 @@ export interface IWindowService {
} }
export interface IWindowSettings { export interface IWindowSettings {
openFilesInNewWindow: boolean; openFilesInNewWindow: 'on' | 'off' | 'default';
openFoldersInNewWindow: 'on' | 'off' | 'default';
reopenFolders: 'all' | 'one' | 'none'; reopenFolders: 'all' | 'one' | 'none';
restoreFullscreen: boolean; restoreFullscreen: boolean;
zoomLevel: number; 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: 'setDocumentEdited', arg: [number, boolean]): TPromise<void>;
call(command: 'toggleMenuBar', arg: number): TPromise<void>; call(command: 'toggleMenuBar', arg: number): TPromise<void>;
call(command: 'quit'): 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: 'openNewWindow'): TPromise<void>;
call(command: 'showWindow', arg: number): TPromise<void>; call(command: 'showWindow', arg: number): TPromise<void>;
call(command: 'getWindows'): TPromise<{ id: number; path: string; title: string; }[]>; 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 'unmaximizeWindow': return this.service.unmaximizeWindow(arg);
case 'setDocumentEdited': return this.service.setDocumentEdited(arg[0], arg[1]); case 'setDocumentEdited': return this.service.setDocumentEdited(arg[0], arg[1]);
case 'toggleMenuBar': return this.service.toggleMenuBar(arg); 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 'openNewWindow': return this.service.openNewWindow();
case 'showWindow': return this.service.showWindow(arg); case 'showWindow': return this.service.showWindow(arg);
case 'getWindows': return this.service.getWindows(); case 'getWindows': return this.service.getWindows();
@@ -179,8 +179,8 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('quit'); return this.channel.call('quit');
} }
windowOpen(paths: string[], forceNewWindow?: boolean): TPromise<void> { openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void> {
return this.channel.call('windowOpen', [paths, forceNewWindow]); return this.channel.call('openWindow', [paths, options]);
} }
openNewWindow(): TPromise<void> { openNewWindow(): TPromise<void> {
@@ -16,7 +16,7 @@ import { fromEventEmitter } from 'vs/base/node/event';
import { IURLService } from 'vs/platform/url/common/url'; import { IURLService } from 'vs/platform/url/common/url';
// TODO@Joao: remove this dependency, move all implementation to this class // 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 { export class WindowsService implements IWindowsService, IDisposable {
@@ -94,7 +94,7 @@ export class WindowsService implements IWindowsService, IDisposable {
const vscodeWindow = this.windowsMainService.getWindowById(windowId); const vscodeWindow = this.windowsMainService.getWindowById(windowId);
if (vscodeWindow) { 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); return TPromise.as(null);
@@ -198,17 +198,17 @@ export class WindowsService implements IWindowsService, IDisposable {
return TPromise.as(null); 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) { if (!paths || !paths.length) {
return TPromise.as(null); 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); return TPromise.as(null);
} }
openNewWindow(): TPromise<void> { openNewWindow(): TPromise<void> {
this.windowsMainService.openNewWindow(); this.windowsMainService.openNewWindow(OpenContext.OTHER);
return TPromise.as(null); 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 cli = assign(Object.create(null), this.environmentService.args, { goto: true });
const pathsToOpen = [filePath]; const pathsToOpen = [filePath];
this.windowsMainService.open({ cli, pathsToOpen }); this.windowsMainService.open({ context: OpenContext.OTHER, cli, pathsToOpen });
return TPromise.as(null); return TPromise.as(null);
} }
@@ -579,7 +579,7 @@ export class MainThreadEditorsTracker {
} }
private _findVisibleTextEditorIds(): string[] { private _findVisibleTextEditorIds(): string[] {
let result = []; let result: string[] = [];
let modelUris = Object.keys(this._model2TextEditors); let modelUris = Object.keys(this._model2TextEditors);
for (let i = 0, len = modelUris.length; i < len; i++) { for (let i = 0, len = modelUris.length; i < len; i++) {
let editors = this._model2TextEditors[modelUris[i]]; let editors = this._model2TextEditors[modelUris[i]];
@@ -16,7 +16,7 @@ export class MainThreadTreeExplorers extends MainThreadTreeExplorersShape {
private _proxy: ExtHostTreeExplorersShape; private _proxy: ExtHostTreeExplorersShape;
constructor( constructor(
@IThreadService private threadService: IThreadService, @IThreadService threadService: IThreadService,
@ITreeExplorerService private treeExplorerService: ITreeExplorerService, @ITreeExplorerService private treeExplorerService: ITreeExplorerService,
@IMessageService private messageService: IMessageService, @IMessageService private messageService: IMessageService,
@ICommandService private commandService: ICommandService @ICommandService private commandService: ICommandService
@@ -30,9 +30,9 @@ export class MainThreadWorkspace extends MainThreadWorkspaceShape {
constructor( constructor(
@ISearchService searchService: ISearchService, @ISearchService searchService: ISearchService,
@IWorkspaceContextService contextService: IWorkspaceContextService, @IWorkspaceContextService contextService: IWorkspaceContextService,
@ITextFileService textFileService, @ITextFileService textFileService: ITextFileService,
@IWorkbenchEditorService editorService, @IWorkbenchEditorService editorService: IWorkbenchEditorService,
@ITextModelResolverService textModelResolverService, @ITextModelResolverService textModelResolverService: ITextModelResolverService,
@IFileService fileService: IFileService @IFileService fileService: IFileService
) { ) {
super(); 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); .then(() => true);
} }
} }
@@ -92,7 +92,7 @@ export class ContributableActionProvider implements IActionProvider {
private registry: IActionBarRegistry; private registry: IActionBarRegistry;
constructor() { constructor() {
this.registry = (<IActionBarRegistry>Registry.as(Extensions.Actionbar)); this.registry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
} }
private toContext(tree: ITree, element: any): any { 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); jsonRegistry.registerSchema(schemaId, schema);

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