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