Merge branch 'master' into isidorn/multiRootDnD

This commit is contained in:
isidor 2018-03-13 12:17:52 +01:00
commit 5a8abd64bb
172 changed files with 2640 additions and 1237 deletions

View file

@ -6,4 +6,6 @@
'2018-02-19 16:00, Europe/Zurich': 'development',
'2018-02-26 18:00, US/Pacific': 'endgame',
'2018-03-07 12:00, US/Pacific': 'release', # 1.21.0
'2018-03-12 12:00, US/Pacific': 'development',
'2018-03-26 18:00, US/Pacific': 'endgame',
}

View file

@ -1,7 +1,7 @@
[
{
"name": "ms-vscode.node-debug",
"version": "1.22.1",
"version": "1.22.2",
"repo": "https://github.com/Microsoft/vscode-node-debug"
},
{

View file

@ -6,7 +6,7 @@
import 'mocha';
import * as assert from 'assert';
import { getCSSLanguageService, getSCSSLanguageService } from 'vscode-css-languageservice/lib/cssLanguageService';
import { getCSSLanguageService, getSCSSLanguageService } from 'vscode-css-languageservice';
import { TextDocument, CompletionList } from 'vscode-languageserver-types';
import { getEmmetCompletionParticipants } from 'vscode-emmet-helper';
@ -24,14 +24,14 @@ suite('Emmet Support', () => {
const emmetCompletionList: CompletionList = {
isIncomplete: true,
items: undefined
}
};
const languageService = syntax === 'scss' ? scssLanguageService : cssLanguageService;
languageService.setCompletionParticipants([getEmmetCompletionParticipants(document, position, document.languageId, {}, emmetCompletionList)])
languageService.setCompletionParticipants([getEmmetCompletionParticipants(document, position, document.languageId, {}, emmetCompletionList)]);
const stylesheet = languageService.parseStylesheet(document);
const list = languageService.doComplete!(document, position, stylesheet);
assert.ok(list);
assert.ok(emmetCompletionList)
assert.ok(emmetCompletionList);
if (expectedProposal && expectedProposalDoc) {
let actualLabels = (emmetCompletionList!.items || []).map(c => c.label).sort();

View file

@ -330,7 +330,103 @@
"title": "%command.reflectCSSValue%",
"category": "Emmet"
}
]
],
"menus": {
"commandPalette": [
{
"command": "editor.emmet.action.wrapIndividualLinesWithAbbreviation",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.wrapWithAbbreviation",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.removeTag",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.updateTag",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.matchTag",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.balanceIn",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.balanceOut",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.prevEditPoint",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.nextEditPoint",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.mergeLines",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.selectPrevItem",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.selectNextItem",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.splitJoinTag",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.toggleComment",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.evaluateMathExpression",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.updateImageSize",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.incrementNumberByOneTenth",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.incrementNumberByOne",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.incrementNumberByTen",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.decrementNumberByOneTenth",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.decrementNumberByOne",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.decrementNumberByTen",
"when": "resourceScheme =~ /^untitled$|^file$/"
},
{
"command": "editor.emmet.action.reflectCSSValue",
"when": "resourceScheme =~ /^untitled$|^file$/"
}
]
}
},
"scripts": {
"compile": "gulp compile-extension:emmet"

View file

@ -51,7 +51,11 @@ export function wrapWithAbbreviation(args: any) {
editor.selections.sort((a: vscode.Selection, b: vscode.Selection) => { return a.start.line - b.start.line; }).forEach(selection => {
let rangeToReplace: vscode.Range = selection.isReversed ? new vscode.Range(selection.active, selection.anchor) : selection;
if (rangeToReplace.isEmpty) {
if (!rangeToReplace.isSingleLine && rangeToReplace.end.character === 0) {
let previousLine = rangeToReplace.end.line - 1;
let lastChar = editor.document.lineAt(previousLine).text.length;
rangeToReplace = new vscode.Range(rangeToReplace.start, new vscode.Position(previousLine, lastChar));
} else if (rangeToReplace.isEmpty) {
let { active } = selection;
let currentNode = getNode(rootNode, active, true);
if (currentNode && (currentNode.start.line === active.line || currentNode.end.line === active.line)) {
@ -61,11 +65,7 @@ export function wrapWithAbbreviation(args: any) {
}
}
const firstLineOfSelection = editor.document.lineAt(rangeToReplace.start).text.substr(rangeToReplace.start.character);
const matches = firstLineOfSelection.match(/^(\s*)/);
const extraWhiteSpaceSelected = matches ? matches[1].length : 0;
rangeToReplace = new vscode.Range(rangeToReplace.start.line, rangeToReplace.start.character + extraWhiteSpaceSelected, rangeToReplace.end.line, rangeToReplace.end.character);
rangeToReplace = ignoreExtraWhitespaceSelected(rangeToReplace, editor.document);
const wholeFirstLine = editor.document.lineAt(rangeToReplace.start).text;
const otherMatches = wholeFirstLine.match(/^(\s*)/);
@ -184,10 +184,26 @@ export function wrapIndividualLinesWithAbbreviation(args: any) {
}
const editor = vscode.window.activeTextEditor;
if (editor.selection.isEmpty) {
if (editor.selections.length === 1 && editor.selection.isEmpty) {
vscode.window.showInformationMessage('Select more than 1 line and try again.');
return;
}
if (editor.selections.find(x => x.isEmpty)) {
vscode.window.showInformationMessage('Select more than 1 line in each selection and try again.');
return;
}
let rangesToReplace: vscode.Range[] = [];
editor.selections.forEach(selection => {
let rangeToReplace: vscode.Range = selection.isReversed ? new vscode.Range(selection.active, selection.anchor) : selection;
if (!rangeToReplace.isSingleLine && rangeToReplace.end.character === 0) {
let previousLine = rangeToReplace.end.line - 1;
let lastChar = editor.document.lineAt(previousLine).text.length;
rangeToReplace = new vscode.Range(rangeToReplace.start, new vscode.Position(previousLine, lastChar));
}
rangeToReplace = ignoreExtraWhitespaceSelected(rangeToReplace, editor.document);
rangesToReplace.push(rangeToReplace);
});
const syntax = getSyntaxFromArgs({ language: editor.document.languageId });
if (!syntax) {
@ -195,31 +211,43 @@ export function wrapIndividualLinesWithAbbreviation(args: any) {
}
const abbreviationPromise = (args && args['abbreviation']) ? Promise.resolve(args['abbreviation']) : vscode.window.showInputBox({ prompt: 'Enter Abbreviation' });
const lines = editor.document.getText(editor.selection).split('\n').map(x => x.trim());
const helper = getEmmetHelper();
return abbreviationPromise.then(inputAbbreviation => {
let expandAbbrInput: ExpandAbbreviationInput[] = [];
if (!inputAbbreviation || !inputAbbreviation.trim() || !helper.isAbbreviationValid(syntax, inputAbbreviation)) { return false; }
let extractedResults = helper.extractAbbreviationFromText(inputAbbreviation);
if (!extractedResults) {
return false;
}
rangesToReplace.forEach(rangeToReplace => {
let lines = editor.document.getText(rangeToReplace).split('\n').map(x => x.trim());
let { abbreviation, filter } = extractedResults;
let input: ExpandAbbreviationInput = {
syntax,
abbreviation,
rangeToReplace: editor.selection,
textToWrap: lines,
filter
};
let { abbreviation, filter } = extractedResults;
let input: ExpandAbbreviationInput = {
syntax,
abbreviation,
rangeToReplace,
textToWrap: lines,
filter
};
expandAbbrInput.push(input);
});
return expandAbbreviationInRange(editor, [input], true);
return expandAbbreviationInRange(editor, expandAbbrInput, false);
});
}
function ignoreExtraWhitespaceSelected(range: vscode.Range, document: vscode.TextDocument): vscode.Range {
const firstLineOfSelection = document.lineAt(range.start).text.substr(range.start.character);
const matches = firstLineOfSelection.match(/^(\s*)/);
const extraWhiteSpaceSelected = matches ? matches[1].length : 0;
return new vscode.Range(range.start.line, range.start.character + extraWhiteSpaceSelected, range.end.line, range.end.character);
}
export function expandEmmetAbbreviation(args: any): Thenable<boolean | undefined> {
if (!validate() || !vscode.window.activeTextEditor) {
return fallbackTab();
@ -446,6 +474,10 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen
let valid = true;
let foundSpace = false; // If < is found before finding whitespace, then its valid abbreviation. Eg: <div|
let i = textToBackTrack.length - 1;
if (textToBackTrack[i] === startAngle) {
return false;
}
while (i >= 0) {
const char = textToBackTrack[i];
i--;
@ -493,10 +525,10 @@ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: Ex
// We will not be able to maintain multiple cursors after snippet insertion
let insertPromises: Thenable<boolean>[] = [];
if (!insertSameSnippet) {
expandAbbrList.forEach((expandAbbrInput: ExpandAbbreviationInput) => {
expandAbbrList.sort((a: ExpandAbbreviationInput, b: ExpandAbbreviationInput) => { return b.rangeToReplace.start.compareTo(a.rangeToReplace.start); }).forEach((expandAbbrInput: ExpandAbbreviationInput) => {
let expandedText = expandAbbr(expandAbbrInput);
if (expandedText) {
insertPromises.push(editor.insertSnippet(new vscode.SnippetString(expandedText), expandAbbrInput.rangeToReplace));
insertPromises.push(editor.insertSnippet(new vscode.SnippetString(expandedText), expandAbbrInput.rangeToReplace, { undoStopBefore: false, undoStopAfter: false }));
}
});
if (insertPromises.length === 0) {

View file

@ -4,11 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { HtmlNode, Node } from 'EmmetNode';
import { Node } from 'EmmetNode';
import { isValidLocationForEmmetAbbreviation } from './abbreviationActions';
import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, getEmmetConfiguration, getEmmetMode, isStyleSheet } from './util';
const allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template', 'text/template'];
import { getEmmetHelper, getNode, getMappingForIncludedLanguages, parseDocument, getEmmetConfiguration, getEmmetMode, isStyleSheet } from './util';
export class DefaultCompletionItemProvider implements vscode.CompletionItemProvider {
@ -35,21 +33,22 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
return;
}
// If document can be html/css parsed, validate syntax and location
if (document.languageId === 'html' || isStyleSheet(document.languageId)) {
let validateLocation = syntax === 'html';
let currentNode: Node | null = null;
// If document can be css parsed, get currentNode
if (isStyleSheet(document.languageId)) {
const rootNode = parseDocument(document, false);
if (!rootNode) {
return;
}
// Use syntaxHelper to update sytnax if needed
const currentNode = getNode(rootNode, position, true);
syntax = this.syntaxHelper(syntax, currentNode, position);
currentNode = getNode(rootNode, position, true);
validateLocation = true;
}
// Validate location
if (!syntax || !isValidLocationForEmmetAbbreviation(document, currentNode, syntax, position, extractAbbreviationResults.abbreviationRange)) {
return;
}
if (validateLocation && !isValidLocationForEmmetAbbreviation(document, currentNode, syntax, position, extractAbbreviationResults.abbreviationRange)) {
return;
}
let noiseCheckPromise: Thenable<any> = Promise.resolve();
@ -98,34 +97,7 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi
});
}
/**
* Parses given document to check whether given position is valid for emmet abbreviation and returns appropriate syntax
* @param syntax string language mode of current document
* @param currentNode node in the document that contains the position
* @param position vscode.Position position of the abbreviation that needs to be expanded
*/
private syntaxHelper(syntax: string | undefined, currentNode: Node | null, position: vscode.Position): string | undefined {
if (syntax && !isStyleSheet(syntax)) {
const currentHtmlNode = <HtmlNode>currentNode;
if (currentHtmlNode && currentHtmlNode.close) {
const innerRange = getInnerRange(currentHtmlNode);
if (innerRange && innerRange.contains(position)) {
if (currentHtmlNode.name === 'style') {
return 'css';
}
if (currentHtmlNode.name === 'script') {
if (currentHtmlNode.attributes
&& currentHtmlNode.attributes.some(x => x.name.toString() === 'type' && allowedMimeTypesInScriptTag.indexOf(x.value.toString()) > -1)) {
return syntax;
}
return;
}
}
}
}
return syntax;
}

View file

@ -227,32 +227,32 @@ suite('Tests for Expand Abbreviations (HTML)', () => {
});
});
test('Expand css when inside style tag in completion list (HTML)', () => {
const abbreviation = 'm10';
const expandedText = 'margin: 10px;';
// test('Expand css when inside style tag in completion list (HTML)', () => {
// const abbreviation = 'm10';
// const expandedText = 'margin: 10px;';
return withRandomFileEditor(htmlContents, 'html', (editor, doc) => {
editor.selection = new Selection(13, 3, 13, 6);
const cancelSrc = new CancellationTokenSource();
const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token);
if (!completionPromise) {
assert.equal(1, 2, `Problem with expanding m10`);
return Promise.resolve();
}
// return withRandomFileEditor(htmlContents, 'html', (editor, doc) => {
// editor.selection = new Selection(13, 3, 13, 6);
// const cancelSrc = new CancellationTokenSource();
// const completionPromise = completionProvider.provideCompletionItems(editor.document, editor.selection.active, cancelSrc.token);
// if (!completionPromise) {
// assert.equal(1, 2, `Problem with expanding m10`);
// return Promise.resolve();
// }
return completionPromise.then((completionList: CompletionList) => {
if (!completionList.items || !completionList.items.length) {
assert.equal(1, 2, `Problem with expanding m10`);
return Promise.resolve();
}
const emmetCompletionItem = completionList.items[0];
assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
assert.equal((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`);
return Promise.resolve();
});
});
});
// return completionPromise.then((completionList: CompletionList) => {
// if (!completionList.items || !completionList.items.length) {
// assert.equal(1, 2, `Problem with expanding m10`);
// return Promise.resolve();
// }
// const emmetCompletionItem = completionList.items[0];
// assert.equal(emmetCompletionItem.label, expandedText, `Label of completion item doesnt match.`);
// assert.equal((<string>emmetCompletionItem.documentation || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`);
// assert.equal(emmetCompletionItem.filterText, abbreviation, `FilterText of completion item doesnt match.`);
// return Promise.resolve();
// });
// });
// });
test('No expanding when html is excluded in the settings', () => {
return workspace.getConfiguration('emmet').update('excludeLanguages', ['html']).then(() => {

View file

@ -61,7 +61,7 @@ suite('Tests for Wrap with Abbreviations', () => {
const multiCursors = [new Selection(2, 6, 2, 6), new Selection(3, 6, 3, 6)];
const multiCursorsWithSelection = [new Selection(2, 2, 2, 28), new Selection(3, 2, 3, 33)];
const multiCursorsWithFullLineSelection = [new Selection(2, 0, 2, 28), new Selection(3, 0, 3, 33)];
const multiCursorsWithFullLineSelection = [new Selection(2, 0, 2, 28), new Selection(3, 0, 4, 0)];
test('Wrap with block element using multi cursor', () => {
@ -251,6 +251,35 @@ suite('Tests for Wrap with Abbreviations', () => {
});
});
test('Wrap individual lines with abbreviation with extra space selected', () => {
const contents = `
<ul class="nav main">
<li class="item1">img</li>
<li class="item2">hi.there</li>
</ul>
`;
const wrapIndividualLinesExpected = `
<ul class="nav main">
<ul>
<li class="hello1"><li class="item1">img</li></li>
<li class="hello2"><li class="item2">hi.there</li></li>
</ul>
</ul>
`;
return withRandomFileEditor(contents, 'html', (editor, doc) => {
editor.selections = [new Selection(2, 1, 4, 0)];
const promise = wrapIndividualLinesWithAbbreviation({ abbreviation: 'ul>li.hello$*' });
if (!promise) {
assert.equal(1, 2, 'Wrap Individual Lines with Abbreviation returned undefined.');
return Promise.resolve();
}
return promise.then(() => {
assert.equal(editor.document.getText(), wrapIndividualLinesExpected);
return Promise.resolve();
});
});
});
test('Wrap individual lines with abbreviation with comment filter', () => {
const contents = `
<ul class="nav main">

View file

@ -9,7 +9,7 @@
},
"dependencies": {
"vscode-css-languageservice": "^3.0.6",
"vscode-emmet-helper": "1.2.0",
"vscode-emmet-helper": "1.2.1",
"vscode-html-languageservice": "^2.0.17-next.3",
"vscode-languageserver": "4.0.0-next.4",
"vscode-languageserver-types": "^3.6.0-next.1",

View file

@ -22,15 +22,10 @@ export function getPathCompletionParticipant(
onHtmlAttributeValue: ({ tag, attribute, value, range }) => {
if (shouldDoPathCompletion(tag, attribute, value)) {
let workspaceRoot;
if (startsWith(value, '/')) {
if (!workspaceFolders || workspaceFolders.length === 0) {
return;
}
workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders);
if (!workspaceFolders || workspaceFolders.length === 0) {
return;
}
const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders);
const suggestions = providePathSuggestions(value, range, URI.parse(document.uri).fsPath, workspaceRoot);
result.items = [...suggestions, ...result.items];
@ -56,34 +51,49 @@ function shouldDoPathCompletion(tag: string, attr: string, value: string): boole
}
export function providePathSuggestions(value: string, range: Range, activeDocFsPath: string, root?: string): CompletionItem[] {
if (value.indexOf('/') === -1) {
return [];
}
if (startsWith(value, '/') && !root) {
return [];
}
let replaceRange: Range;
const lastIndexOfSlash = value.lastIndexOf('/');
const valueBeforeLastSlash = value.slice(0, lastIndexOfSlash + 1);
const valueAfterLastSlash = value.slice(lastIndexOfSlash + 1);
const parentDir = startsWith(value, '/')
? path.resolve(root, '.' + valueBeforeLastSlash)
: path.resolve(activeDocFsPath, '..', valueBeforeLastSlash);
if (!fs.existsSync(parentDir)) {
return [];
if (lastIndexOfSlash === -1) {
replaceRange = getFullReplaceRange(range);
} else {
const valueAfterLastSlash = value.slice(lastIndexOfSlash + 1);
replaceRange = getReplaceRange(range, valueAfterLastSlash);
}
const replaceRange = getReplaceRange(range, valueAfterLastSlash);
let parentDir: string;
if (lastIndexOfSlash === -1) {
parentDir = path.resolve(root);
} else {
const valueBeforeLastSlash = value.slice(0, lastIndexOfSlash + 1);
parentDir = startsWith(value, '/')
? path.resolve(root, '.' + valueBeforeLastSlash)
: path.resolve(activeDocFsPath, '..', valueBeforeLastSlash);
}
try {
return fs.readdirSync(parentDir).map(f => {
return {
label: f,
kind: isDir(path.resolve(parentDir, f)) ? CompletionItemKind.Folder : CompletionItemKind.File,
textEdit: TextEdit.replace(replaceRange, f)
};
if (isDir(path.resolve(parentDir, f))) {
return {
label: f + '/',
kind: CompletionItemKind.Folder,
textEdit: TextEdit.replace(replaceRange, f + '/'),
command: {
title: 'Suggest',
command: 'editor.action.triggerSuggest'
}
};
} else {
return {
label: f,
kind: CompletionItemKind.File,
textEdit: TextEdit.replace(replaceRange, f)
};
}
});
} catch (e) {
return [];
@ -102,7 +112,12 @@ function resolveWorkspaceRoot(activeDoc: TextDocument, workspaceFolders: Propose
}
}
function getReplaceRange(valueRange: Range, valueAfterLastSlash: string): Range {
function getFullReplaceRange(valueRange: Range) {
const start = Position.create(valueRange.end.line, valueRange.start.character + 1);
const end = Position.create(valueRange.end.line, valueRange.end.character - 1);
return Range.create(start, end);
}
function getReplaceRange(valueRange: Range, valueAfterLastSlash: string) {
const start = Position.create(valueRange.end.line, valueRange.end.character - 1 - valueAfterLastSlash.length);
const end = Position.create(valueRange.end.line, valueRange.end.character - 1);
return Range.create(start, end);

View file

@ -7,78 +7,117 @@
import * as assert from 'assert';
import * as path from 'path';
import { providePathSuggestions } from '../../modes/pathCompletion';
import { CompletionItemKind, Range, Position } from 'vscode-languageserver-types';
import { CompletionItemKind, Range, Position, CompletionItem, TextEdit, Command } from 'vscode-languageserver-types';
const fixtureRoot = path.resolve(__dirname, '../../../test/pathCompletionFixtures');
suite('Path Completion - Relative Path', () => {
const mockRange = Range.create(Position.create(0, 3), Position.create(0, 5));
function toRange(line: number, startChar: number, endChar: number) {
return Range.create(Position.create(line, startChar), Position.create(line, endChar));
}
function toTextEdit(line: number, startChar: number, endChar: number, newText: string) {
const range = Range.create(Position.create(line, startChar), Position.create(line, endChar));
return TextEdit.replace(range, newText);
}
interface PathSuggestion {
label?: string;
kind?: CompletionItemKind;
textEdit?: TextEdit;
command?: Command;
}
function assertSuggestions(actual: CompletionItem[], expected: PathSuggestion[]) {
assert.equal(actual.length, expected.length, `Suggestions have length ${actual.length} but should have length ${expected.length}`);
for (let i = 0; i < expected.length; i++) {
if (expected[i].label) {
assert.equal(
actual[i].label,
expected[i].label,
`Suggestion ${actual[i].label} should have label ${expected[i].label}`
);
}
if (expected[i].kind) {
assert.equal(actual[i].kind,
expected[i].kind,
`Suggestion ${actual[i].label} has type ${CompletionItemKind[actual[i].kind]} but should have label ${CompletionItemKind[expected[i].kind]}`
);
}
if (expected[i].textEdit) {
assert.equal(actual[i].textEdit.newText, expected[i].textEdit.newText);
assert.deepEqual(actual[i].textEdit.range, expected[i].textEdit.range);
}
if (expected[i].command) {
assert.equal(
actual[i].command.title,
expected[i].command.title,
`Suggestion ${actual[i].label} has command title ${actual[i].command.title} but should have command title ${expected[i].command.title}`
);
assert.equal(
actual[i].command.command,
expected[i].command.command,
`Suggestion ${actual[i].label} has command ${actual[i].command.command} but should have command ${expected[i].command.command}`
);
}
}
}
suite('Path Completion - Relative Path:', () => {
const mockRange = toRange(0, 3, 5);
test('Current Folder', () => {
const value = './';
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
assert.equal(suggestions.length, 3);
assert.equal(suggestions[0].label, 'about');
assert.equal(suggestions[1].label, 'index.html');
assert.equal(suggestions[2].label, 'src');
assert.equal(suggestions[0].kind, CompletionItemKind.Folder);
assert.equal(suggestions[1].kind, CompletionItemKind.File);
assert.equal(suggestions[2].kind, CompletionItemKind.Folder);
assertSuggestions(suggestions, [
{ label: 'about/', kind: CompletionItemKind.Folder },
{ label: 'index.html', kind: CompletionItemKind.File },
{ label: 'src/', kind: CompletionItemKind.Folder }
]);
});
test('Parent Folder', () => {
test('Parent Folder:', () => {
const value = '../';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
assert.equal(suggestions.length, 3);
assert.equal(suggestions[0].label, 'about');
assert.equal(suggestions[1].label, 'index.html');
assert.equal(suggestions[2].label, 'src');
assert.equal(suggestions[0].kind, CompletionItemKind.Folder);
assert.equal(suggestions[1].kind, CompletionItemKind.File);
assert.equal(suggestions[2].kind, CompletionItemKind.Folder);
assertSuggestions(suggestions, [
{ label: 'about/', kind: CompletionItemKind.Folder },
{ label: 'index.html', kind: CompletionItemKind.File },
{ label: 'src/', kind: CompletionItemKind.Folder }
]);
});
test('Adjacent Folder', () => {
test('Adjacent Folder:', () => {
const value = '../src/';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
assert.equal(suggestions.length, 2);
assert.equal(suggestions[0].label, 'feature.js');
assert.equal(suggestions[1].label, 'test.js');
assert.equal(suggestions[0].kind, CompletionItemKind.File);
assert.equal(suggestions[1].kind, CompletionItemKind.File);
assertSuggestions(suggestions, [
{ label: 'feature.js', kind: CompletionItemKind.File },
{ label: 'test.js', kind: CompletionItemKind.File }
]);
});
});
suite('Path Completion - Absolute Path', () => {
const mockRange = Range.create(Position.create(0, 3), Position.create(0, 5));
suite('Path Completion - Absolute Path:', () => {
const mockRange = toRange(0, 3, 5);
test('Root', () => {
const value = '/';
const activeFileFsPath1 = path.resolve(fixtureRoot, 'index.html');
const activeFileFsPath2 = path.resolve(fixtureRoot, 'about/index.html');
const suggestions1 = providePathSuggestions(value, mockRange, activeFileFsPath1, fixtureRoot);
const suggestions2 = providePathSuggestions(value, mockRange, activeFileFsPath2, fixtureRoot);
const suggestions1 = providePathSuggestions(value, mockRange, activeFileFsPath1, fixtureRoot);
const suggestions2 = providePathSuggestions(value, mockRange, activeFileFsPath2, fixtureRoot);
const verify = (suggestions) => {
assert.equal(suggestions[0].label, 'about');
assert.equal(suggestions[1].label, 'index.html');
assert.equal(suggestions[2].label, 'src');
assert.equal(suggestions[0].kind, CompletionItemKind.Folder);
assert.equal(suggestions[1].kind, CompletionItemKind.File);
assert.equal(suggestions[2].kind, CompletionItemKind.Folder);
assertSuggestions(suggestions, [
{ label: 'about/', kind: CompletionItemKind.Folder },
{ label: 'index.html', kind: CompletionItemKind.File },
{ label: 'src/', kind: CompletionItemKind.Folder }
]);
};
verify(suggestions1);
@ -90,62 +129,109 @@ suite('Path Completion - Absolute Path', () => {
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath, fixtureRoot);
assert.equal(suggestions.length, 2);
assert.equal(suggestions[0].label, 'feature.js');
assert.equal(suggestions[1].label, 'test.js');
assert.equal(suggestions[0].kind, CompletionItemKind.File);
assert.equal(suggestions[1].kind, CompletionItemKind.File);
assertSuggestions(suggestions, [
{ label: 'feature.js', kind: CompletionItemKind.File },
{ label: 'test.js', kind: CompletionItemKind.File }
]);
});
});
suite('Path Completion - Incomplete Path at End', () => {
const mockRange = Range.create(Position.create(0, 3), Position.create(0, 5));
suite('Path Completion - Folder Commands:', () => {
const mockRange = toRange(0, 3, 5);
test('Folder should have command `editor.action.triggerSuggest', () => {
const value = './';
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath);
assertSuggestions(suggestions, [
{ label: 'about/', command: { title: 'Suggest', command: 'editor.action.triggerSuggest'} },
{ label: 'index.html' },
{ label: 'src/', command: { title: 'Suggest', command: 'editor.action.triggerSuggest'} },
]);
});
});
suite('Path Completion - Incomplete Path at End:', () => {
const mockRange = toRange(0, 3, 5);
test('Incomplete Path that starts with slash', () => {
const value = '/src/f';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath, fixtureRoot);
assert.equal(suggestions.length, 2);
assert.equal(suggestions[0].label, 'feature.js');
assert.equal(suggestions[1].label, 'test.js');
assert.equal(suggestions[0].kind, CompletionItemKind.File);
assert.equal(suggestions[1].kind, CompletionItemKind.File);
});
assertSuggestions(suggestions, [
{ label: 'feature.js', kind: CompletionItemKind.File },
{ label: 'test.js', kind: CompletionItemKind.File }
]);
});
test('Incomplete Path that does not start with slash', () => {
const value = '../src/f';
const activeFileFsPath = path.resolve(fixtureRoot, 'about/about.html');
const suggestions = providePathSuggestions(value, mockRange, activeFileFsPath, fixtureRoot);
assert.equal(suggestions.length, 2);
assert.equal(suggestions[0].label, 'feature.js');
assert.equal(suggestions[1].label, 'test.js');
assert.equal(suggestions[0].kind, CompletionItemKind.File);
assert.equal(suggestions[1].kind, CompletionItemKind.File);
});
assertSuggestions(suggestions, [
{ label: 'feature.js', kind: CompletionItemKind.File },
{ label: 'test.js', kind: CompletionItemKind.File }
]);
});
});
suite('Path Completion - TextEdit', () => {
suite('Path Completion - No leading dot or slash:', () => {
test('Top level completion', () => {
const value = 's';
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
const range = toRange(0, 3, 5);
const suggestions = providePathSuggestions(value, range, activeFileFsPath, fixtureRoot);
assertSuggestions(suggestions, [
{ label: 'about/', kind: CompletionItemKind.Folder, textEdit: toTextEdit(0, 4, 4, 'about/') },
{ label: 'index.html', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 4, 4, 'index.html') },
{ label: 'src/', kind: CompletionItemKind.Folder, textEdit: toTextEdit(0, 4, 4, 'src/') }
]);
});
test('src/', () => {
const value = 'src/';
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
const range = toRange(0, 3, 8);
const suggestions = providePathSuggestions(value, range, activeFileFsPath, fixtureRoot);
assertSuggestions(suggestions, [
{ label: 'feature.js', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 7, 7, 'feature.js') },
{ label: 'test.js', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 7, 7, 'test.js') }
]);
});
test('src/f', () => {
const value = 'src/f';
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
const range = toRange(0, 3, 9);
const suggestions = providePathSuggestions(value, range, activeFileFsPath, fixtureRoot);
assertSuggestions(suggestions, [
{ label: 'feature.js', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 7, 8, 'feature.js') },
{ label: 'test.js', kind: CompletionItemKind.File, textEdit: toTextEdit(0, 7, 8, 'test.js') }
]);
});
});
suite('Path Completion - TextEdit:', () => {
test('TextEdit has correct replace text and range', () => {
const value = './';
const activeFileFsPath = path.resolve(fixtureRoot, 'index.html');
const range = Range.create(Position.create(0, 3), Position.create(0, 5));
const suggestions = providePathSuggestions(value, range, activeFileFsPath);
assert.equal(suggestions[0].textEdit.newText, 'about');
assert.equal(suggestions[1].textEdit.newText, 'index.html');
assert.equal(suggestions[2].textEdit.newText, 'src');
const range = toRange(0, 3, 5);
const expectedReplaceRange = toRange(0, 4, 4);
assert.equal(suggestions[0].textEdit.range.start.character, 4);
assert.equal(suggestions[1].textEdit.range.start.character, 4);
assert.equal(suggestions[2].textEdit.range.start.character, 4);
const suggestions = providePathSuggestions(value, range, activeFileFsPath);
assert.equal(suggestions[0].textEdit.range.end.character, 4);
assert.equal(suggestions[1].textEdit.range.end.character, 4);
assert.equal(suggestions[2].textEdit.range.end.character, 4);
assertSuggestions(suggestions, [
{ textEdit: TextEdit.replace(expectedReplaceRange, 'about/') },
{ textEdit: TextEdit.replace(expectedReplaceRange, 'index.html') },
{ textEdit: TextEdit.replace(expectedReplaceRange, 'src/') },
]);
});
});

View file

@ -25,9 +25,9 @@ vscode-css-languageservice@^3.0.6:
vscode-languageserver-types "^3.6.0-next.1"
vscode-nls "^2.0.1"
vscode-emmet-helper@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.0.tgz#6b9311be065c9c99d5de2dae18ea0730d9cfb734"
vscode-emmet-helper@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.1.tgz#dc4a3c83a3f1d48f4e9e1a5cce0e63f24b6eb843"
dependencies:
"@emmetio/extract-abbreviation" "0.1.6"
jsonc-parser "^1.0.0"

View file

@ -9,7 +9,7 @@ import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration, TextDocument, FoldingRangeList, FoldingRange, Disposable } from 'vscode';
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient';
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, CancellationToken } from 'vscode-languageclient';
import TelemetryReporter from 'vscode-extension-telemetry';
import { FoldingRangesRequest } from './protocol/foldingProvider.proposed';
@ -70,7 +70,7 @@ export function activate(context: ExtensionContext) {
// The server is implemented in node
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'jsonServerMain.js'));
// The debug options for the server
let debugOptions = { execArgv: ['--nolazy', '--inspect=6046'] };
let debugOptions = { execArgv: ['--nolazy', '--inspect=' + (9000 + Math.round(Math.random() * 10000))] };
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
@ -154,12 +154,15 @@ export function activate(context: ExtensionContext) {
if (enable) {
if (!foldingProviderRegistration) {
foldingProviderRegistration = languages.registerFoldingProvider(documentSelector, {
provideFoldingRanges(document: TextDocument) {
return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }).then(res => {
provideFoldingRanges(document: TextDocument, token: CancellationToken) {
return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }, token).then(res => {
if (res && Array.isArray(res.ranges)) {
return new FoldingRangeList(res.ranges.map(r => new FoldingRange(r.startLine, r.endLine, r.type)));
}
return null;
}, error => {
client.logFailedRequest(FoldingRangesRequest.type, error);
return null;
});
}
});

View file

@ -12,6 +12,7 @@
{ "open": "[", "close": "]", "notIn": ["string"] },
{ "open": "(", "close": ")", "notIn": ["string"] },
{ "open": "'", "close": "'", "notIn": ["string"] },
{ "open": "/*", "close": "*/", "notIn": ["string"] },
{ "open": "\"", "close": "\"", "notIn": ["string", "comment"] },
{ "open": "`", "close": "`", "notIn": ["string", "comment"] }
]

View file

@ -8,15 +8,16 @@
"node": "*"
},
"dependencies": {
"jsonc-parser": "^1.0.2",
"jsonc-parser": "^2.0.0-next.1",
"request-light": "^0.2.2",
"vscode-json-languageservice": "^3.0.8",
"vscode-json-languageservice": "^3.0.12",
"vscode-languageserver": "^4.0.0",
"vscode-nls": "^3.2.1",
"vscode-uri": "^1.0.1"
"vscode-uri": "^1.0.3"
},
"devDependencies": {
"@types/node": "7.0.43"
"@types/node": "7.0.43",
"@types/mocha": "2.2.33"
},
"scripts": {
"compile": "gulp compile-extension:json-server",
@ -24,6 +25,7 @@
"install-service-next": "yarn add vscode-json-languageservice@next",
"install-service-local": "yarn link vscode-json-languageservice",
"install-server-next": "yarn add vscode-languageserver@next",
"install-server-local": "yarn link vscode-languageserver-server"
"install-server-local": "yarn link vscode-languageserver-server",
"test": "npm run compile && ../../../node_modules/.bin/mocha"
}
}

View file

@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* 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, Position, CancellationToken } from 'vscode-languageserver';
import { createScanner, SyntaxKind, ScanError } from 'jsonc-parser';
import { FoldingRangeType, FoldingRange, FoldingRangeList } from './protocol/foldingProvider.proposed';
export function getFoldingRegions(document: TextDocument, cancellationToken: CancellationToken | null) {
let ranges: FoldingRange[] = [];
let stack: FoldingRange[] = [];
let prevStart = -1;
let scanner = createScanner(document.getText(), false);
let token = scanner.scan();
while (token !== SyntaxKind.EOF) {
if (cancellationToken && cancellationToken.isCancellationRequested) {
return null;
}
switch (token) {
case SyntaxKind.OpenBraceToken:
case SyntaxKind.OpenBracketToken: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
stack.push(range);
break;
}
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CloseBracketToken: {
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
if (stack.length > 0 && stack[stack.length - 1].type === type) {
let range = stack.pop();
let line = document.positionAt(scanner.getTokenOffset()).line;
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
range.endLine = line - 1;
ranges.push(range);
prevStart = range.startLine;
}
}
break;
}
case SyntaxKind.BlockCommentTrivia: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (scanner.getTokenError() === ScanError.UnexpectedEndOfComment && startLine + 1 < document.lineCount) {
scanner.setPosition(document.offsetAt(Position.create(startLine + 1, 0)));
} else {
if (startLine < endLine) {
ranges.push({ startLine, endLine, type: FoldingRangeType.Comment });
prevStart = startLine;
}
}
break;
}
case SyntaxKind.LineCommentTrivia: {
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
if (m) {
let line = document.positionAt(scanner.getTokenOffset()).line;
if (m[1]) { // start pattern match
let range = { startLine: line, endLine: line, type: FoldingRangeType.Region };
stack.push(range);
} else {
let i = stack.length - 1;
while (i >= 0 && stack[i].type !== FoldingRangeType.Region) {
i--;
}
if (i >= 0) {
let range = stack[i];
stack.length = i;
if (line > range.startLine && prevStart !== range.startLine) {
range.endLine = line;
ranges.push(range);
prevStart = range.startLine;
}
}
}
}
break;
}
}
token = scanner.scan();
}
return <FoldingRangeList>{ ranges };
}

View file

@ -7,20 +7,20 @@
import {
createConnection, IConnection,
TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, DocumentColorRequest, ColorPresentationRequest,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, DocumentColorRequest, ColorPresentationRequest
} from 'vscode-languageserver';
import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light';
import fs = require('fs');
import * as fs from 'fs';
import URI from 'vscode-uri';
import * as URL from 'url';
import Strings = require('./utils/strings');
import { startsWith } from './utils/strings';
import { formatError, runSafe, runSafeAsync } from './utils/errors';
import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration } from 'vscode-json-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { createScanner, SyntaxKind } from 'jsonc-parser';
import { getFoldingRegions } from './jsonFolding';
import { FoldingRangeType, FoldingRangesRequest, FoldingRange, FoldingRangeList, FoldingProviderServerCapabilities } from './protocol/foldingProvider.proposed';
import { FoldingRangesRequest, FoldingProviderServerCapabilities } from './protocol/foldingProvider.proposed';
interface ISchemaAssociations {
[pattern: string]: string[];
@ -93,14 +93,14 @@ let workspaceContext = {
};
let schemaRequestService = (uri: string): Thenable<string> => {
if (Strings.startsWith(uri, 'file://')) {
if (startsWith(uri, 'file://')) {
let fsPath = URI.parse(uri).fsPath;
return new Promise<string>((c, e) => {
fs.readFile(fsPath, 'UTF-8', (err, result) => {
err ? e('') : c(result.toString());
});
});
} else if (Strings.startsWith(uri, 'vscode://')) {
} else if (startsWith(uri, 'vscode://')) {
return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => {
return responseText;
}, error => {
@ -260,17 +260,21 @@ function validateTextDocument(textDocument: TextDocument): void {
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] });
return;
}
try {
let jsonDocument = getJSONDocument(textDocument);
let jsonDocument = getJSONDocument(textDocument);
let version = textDocument.version;
let documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'ignore' } : { comments: 'error', trailingCommas: 'error' };
languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => {
// Send the computed diagnostics to VSCode.
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
});
} catch (e) {
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e));
}
let documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'ignore' } : { comments: 'error', trailingCommas: 'error' };
languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => {
setTimeout(() => {
let currDocument = documents.get(textDocument.uri);
if (currDocument && currDocument.version === version) {
// Send the computed diagnostics to VSCode.
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}
}, 100);
}, error => {
connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error));
});
}
connection.onDidChangeWatchedFiles((change) => {
@ -282,7 +286,7 @@ connection.onDidChangeWatchedFiles((change) => {
}
});
if (hasChanges) {
documents.all().forEach(validateTextDocument);
documents.all().forEach(triggerValidation);
}
});
@ -320,19 +324,19 @@ connection.onHover(textDocumentPositionParams => {
}, null, `Error while computing hover for ${textDocumentPositionParams.textDocument.uri}`);
});
connection.onDocumentSymbol(documentSymbolParams => {
connection.onDocumentSymbol((documentSymbolParams, token) => {
return runSafe(() => {
let document = documents.get(documentSymbolParams.textDocument.uri);
let jsonDocument = getJSONDocument(document);
return languageService.findDocumentSymbols(document, jsonDocument);
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`);
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onDocumentRangeFormatting(formatParams => {
connection.onDocumentRangeFormatting((formatParams, token) => {
return runSafe(() => {
let document = documents.get(formatParams.textDocument.uri);
return languageService.format(document, formatParams.range, formatParams.options);
}, [], `Error while formatting range for ${formatParams.textDocument.uri}`);
}, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token);
});
connection.onRequest(DocumentColorRequest.type, params => {
@ -346,7 +350,7 @@ connection.onRequest(DocumentColorRequest.type, params => {
}, [], `Error while computing document colors for ${params.textDocument.uri}`);
});
connection.onRequest(ColorPresentationRequest.type, params => {
connection.onRequest(ColorPresentationRequest.type, (params, token) => {
return runSafe(() => {
let document = documents.get(params.textDocument.uri);
if (document) {
@ -354,86 +358,17 @@ connection.onRequest(ColorPresentationRequest.type, params => {
return languageService.getColorPresentations(document, jsonDocument, params.color, params.range);
}
return [];
}, [], `Error while computing color presentations for ${params.textDocument.uri}`);
}, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
});
connection.onRequest(FoldingRangesRequest.type, params => {
connection.onRequest(FoldingRangesRequest.type, (params, token) => {
return runSafe(() => {
let document = documents.get(params.textDocument.uri);
if (document) {
let ranges: FoldingRange[] = [];
let stack: FoldingRange[] = [];
let prevStart = -1;
let scanner = createScanner(document.getText(), false);
let token = scanner.scan();
while (token !== SyntaxKind.EOF) {
switch (token) {
case SyntaxKind.OpenBraceToken:
case SyntaxKind.OpenBracketToken: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
stack.push(range);
break;
}
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CloseBracketToken: {
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
if (stack.length > 0 && stack[stack.length - 1].type === type) {
let range = stack.pop();
let line = document.positionAt(scanner.getTokenOffset()).line;
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
range.endLine = line - 1;
ranges.push(range);
prevStart = range.startLine;
}
}
break;
}
case SyntaxKind.BlockCommentTrivia: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (startLine < endLine) {
ranges.push({ startLine, endLine, type: FoldingRangeType.Comment });
prevStart = startLine;
}
break;
}
case SyntaxKind.LineCommentTrivia: {
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
if (m) {
let line = document.positionAt(scanner.getTokenOffset()).line;
if (m[1]) { // start pattern match
let range = { startLine: line, endLine: line, type: FoldingRangeType.Region };
stack.push(range);
} else {
let i = stack.length - 1;
while (i >= 0 && stack[i].type !== FoldingRangeType.Region) {
i--;
}
if (i >= 0) {
let range = stack[i];
stack.length = i;
if (line > range.startLine && prevStart !== range.startLine) {
range.endLine = line;
ranges.push(range);
prevStart = range.startLine;
}
}
}
}
break;
}
}
token = scanner.scan();
}
return <FoldingRangeList>{ ranges };
return getFoldingRegions(document, token);
}
return null;
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`);
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
});
// Listen on the connection

View file

@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* 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 'mocha';
import * as assert from 'assert';
import { TextDocument } from 'vscode-languageserver';
import { getFoldingRegions } from '../jsonFolding';
interface ExpectedIndentRange {
startLine: number;
endLine: number;
type?: string;
}
function assertRanges(lines: string[], expected: ExpectedIndentRange[]): void {
let document = TextDocument.create('test://foo/bar.json', 'json', 1, lines.join('\n'));
let actual = getFoldingRegions(document, null)!.ranges;
let actualRanges = [];
for (let i = 0; i < actual.length; i++) {
actualRanges[i] = r(actual[i].startLine, actual[i].endLine, actual[i].type);
}
actualRanges = actualRanges.sort((r1, r2) => r1.startLine - r2.startLine);
assert.deepEqual(actualRanges, expected);
}
function r(startLine: number, endLine: number, type?: string): ExpectedIndentRange {
return { startLine, endLine, type };
}
suite('Object Folding', () => {
test('Fold one level', () => {
let range = [
/*0*/'{',
/*1*/'"foo":"bar"',
/*2*/'}'
];
assertRanges(range, [r(0, 1, 'object')]);
});
test('Fold two level', () => {
let range = [
/*0*/'[',
/*1*/'{',
/*2*/'"foo":"bar"',
/*3*/'}',
/*4*/']'
];
assertRanges(range, [r(0, 3, 'array'), r(1, 2, 'object')]);
});
test('Fold Arrays', () => {
let range = [
/*0*/'[',
/*1*/'[',
/*2*/'],[',
/*3*/'1',
/*4*/']',
/*5*/']'
];
assertRanges(range, [r(0, 4, 'array'), r(2, 3, 'array')]);
});
test('Filter start on same line', () => {
let range = [
/*0*/'[[',
/*1*/'[',
/*2*/'],[',
/*3*/'1',
/*4*/']',
/*5*/']]'
];
assertRanges(range, [r(0, 4, 'array'), r(2, 3, 'array')]);
});
test('Fold commment', () => {
let range = [
/*0*/'/*',
/*1*/' multi line',
/*2*/'*/',
];
assertRanges(range, [r(0, 2, 'comment')]);
});
test('Incomplete commment', () => {
let range = [
/*0*/'/*',
/*1*/'{',
/*2*/'"foo":"bar"',
/*3*/'}',
];
assertRanges(range, [r(1, 2, 'object')]);
});
test('Fold regions', () => {
let range = [
/*0*/'// #region',
/*1*/'{',
/*2*/'}',
/*3*/'// #endregion',
];
assertRanges(range, [r(0, 3, 'region')]);
});
});

View file

@ -4,6 +4,8 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { CancellationToken, ResponseError, ErrorCodes } from 'vscode-languageserver';
export function formatError(message: string, err: any): string {
if (err instanceof Error) {
let error = <Error>err;
@ -23,11 +25,31 @@ export function runSafeAsync<T>(func: () => Thenable<T>, errorVal: T, errorMessa
return errorVal;
});
}
export function runSafe<T>(func: () => T, errorVal: T, errorMessage: string): T {
try {
return func();
} catch (e) {
console.error(formatError(errorMessage, e));
return errorVal;
}
}
export function runSafe<T, E>(func: () => T, errorVal: T, errorMessage: string, token: CancellationToken): Thenable<T | ResponseError<E>> {
return new Promise<T | ResponseError<E>>((resolve, reject) => {
setTimeout(() => {
if (token.isCancellationRequested) {
resolve(cancelValue());
} else {
try {
let result = func();
if (token.isCancellationRequested) {
resolve(cancelValue());
return;
} else {
resolve(result);
}
} catch (e) {
console.error(formatError(errorMessage, e));
resolve(errorVal);
}
}
}, 100);
});
}
function cancelValue<E>() {
console.log('cancelled');
return new ResponseError<E>(ErrorCodes.RequestCancelled, 'Request cancelled');
}

View file

@ -0,0 +1,3 @@
--ui tdd
--useColors true
./out/test/**/*.test.js

View file

@ -2,6 +2,10 @@
# yarn lockfile v1
"@types/mocha@2.2.33":
version "2.2.33"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def"
"@types/node@7.0.43":
version "7.0.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c"
@ -48,9 +52,9 @@ https-proxy-agent@2.1.1:
agent-base "^4.1.0"
debug "^3.1.0"
jsonc-parser@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-1.0.2.tgz#3fe86c0237db206fe5693872d93eec3bc3b05055"
jsonc-parser@^2.0.0-next.1:
version "2.0.0-next.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.0-next.1.tgz#445a824f765a96abfbb286d759a9b1d226b18088"
ms@2.0.0:
version "2.0.0"
@ -64,14 +68,14 @@ request-light@^0.2.2:
https-proxy-agent "2.1.1"
vscode-nls "^2.0.2"
vscode-json-languageservice@^3.0.8:
version "3.0.8"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.0.8.tgz#0f585c2a0e75de224c89a4ee516bbf1be15699aa"
vscode-json-languageservice@^3.0.12:
version "3.0.12"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.0.12.tgz#85258632f2f7718028fbdfbb95b4ad009107b821"
dependencies:
jsonc-parser "^1.0.2"
vscode-languageserver-types "^3.6.0"
jsonc-parser "^2.0.0-next.1"
vscode-languageserver-types "^3.6.1"
vscode-nls "^3.2.1"
vscode-uri "^1.0.1"
vscode-uri "^1.0.3"
vscode-jsonrpc@^3.6.0:
version "3.6.0"
@ -88,6 +92,10 @@ vscode-languageserver-types@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0.tgz#0bba63b0fa82a714394a4478f55a596ee4ed7d0a"
vscode-languageserver-types@^3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.1.tgz#4bc06a48dff653495f12f94b8b1e228988a1748d"
vscode-languageserver@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0.tgz#8b792f0d6d10acfe363d02371ed4ce53d08af88a"
@ -106,3 +114,7 @@ vscode-nls@^3.2.1:
vscode-uri@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.1.tgz#11a86befeac3c4aa3ec08623651a3c81a6d0bbc8"
vscode-uri@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.3.tgz#631bdbf716dccab0e65291a8dc25c23232085a52"

View file

@ -300,6 +300,7 @@
"keyword.operator.expression",
"keyword.operator.cast",
"keyword.operator.sizeof",
"keyword.operator.instanceof",
"keyword.operator.logical.python"
],
"settings": {

View file

@ -319,6 +319,7 @@
"keyword.operator.expression",
"keyword.operator.cast",
"keyword.operator.sizeof",
"keyword.operator.instanceof",
"keyword.operator.logical.python"
],
"settings": {

View file

@ -13,7 +13,7 @@ import * as languageModeIds from '../utils/languageModeIds';
import { disposeAll } from '../utils/dipose';
interface IDiagnosticRequestor {
requestDiagnostic(filepath: string): void;
requestDiagnostic(resource: Uri): void;
}
function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined {
@ -94,12 +94,53 @@ class SyncedBuffer {
};
this.client.execute('change', args, false);
}
this.diagnosticRequestor.requestDiagnostic(filePath);
this.diagnosticRequestor.requestDiagnostic(this.document.uri);
}
}
class SyncedBufferMap {
private readonly _map = new Map<string, SyncedBuffer>();
constructor(
private readonly _normalizePath: (resource: Uri) => string | null
) { }
public has(resource: Uri): boolean {
const file = this._normalizePath(resource);
return !!file && this._map.has(file);
}
public get(resource: Uri): SyncedBuffer | undefined {
const file = this._normalizePath(resource);
return file ? this._map.get(file) : undefined;
}
public set(resource: Uri, buffer: SyncedBuffer) {
const file = this._normalizePath(resource);
if (file) {
this._map.set(file, buffer);
}
}
public delete(resource: Uri): void {
const file = this._normalizePath(resource);
if (file) {
this._map.delete(file);
}
}
public get allBuffers(): Iterable<SyncedBuffer> {
return this._map.values();
}
public get allResources(): Iterable<string> {
return this._map.keys();
}
}
export interface Diagnostics {
delete(file: string): void;
delete(resource: Uri): void;
}
export default class BufferSyncSupport {
@ -110,7 +151,7 @@ export default class BufferSyncSupport {
private readonly modeIds: Set<string>;
private readonly diagnostics: Diagnostics;
private readonly disposables: Disposable[] = [];
private readonly syncedBuffers: Map<string, SyncedBuffer>;
private readonly syncedBuffers: SyncedBufferMap;
private pendingDiagnostics = new Map<string, number>();
private readonly diagnosticDelayer: Delayer<any>;
@ -128,7 +169,7 @@ export default class BufferSyncSupport {
this.diagnosticDelayer = new Delayer<any>(300);
this.syncedBuffers = new Map<string, SyncedBuffer>();
this.syncedBuffers = new SyncedBufferMap(path => this.client.normalizePath(path));
}
public listen(): void {
@ -143,12 +184,11 @@ export default class BufferSyncSupport {
}
public handles(resource: Uri): boolean {
const file = this.client.normalizePath(resource);
return !!file && this.syncedBuffers.has(file);
return this.syncedBuffers.has(resource);
}
public reOpenDocuments(): void {
for (const buffer of this.syncedBuffers.values()) {
for (const buffer of this.syncedBuffers.allBuffers) {
buffer.open();
}
}
@ -167,45 +207,37 @@ export default class BufferSyncSupport {
return;
}
const syncedBuffer = new SyncedBuffer(document, filepath, this, this.client);
this.syncedBuffers.set(filepath, syncedBuffer);
this.syncedBuffers.set(resource, syncedBuffer);
syncedBuffer.open();
this.requestDiagnostic(filepath);
this.requestDiagnostic(resource);
}
private onDidCloseTextDocument(document: TextDocument): void {
const filepath = this.client.normalizePath(document.uri);
if (!filepath) {
return;
}
const syncedBuffer = this.syncedBuffers.get(filepath);
const resource = document.uri;
const syncedBuffer = this.syncedBuffers.get(resource);
if (!syncedBuffer) {
return;
}
this.diagnostics.delete(filepath);
this.syncedBuffers.delete(filepath);
this.diagnostics.delete(resource);
this.syncedBuffers.delete(resource);
syncedBuffer.close();
if (!fs.existsSync(filepath)) {
if (!fs.existsSync(resource.fsPath)) {
this.requestAllDiagnostics();
}
}
private onDidChangeTextDocument(e: TextDocumentChangeEvent): void {
const filepath = this.client.normalizePath(e.document.uri);
if (!filepath) {
return;
const syncedBuffer = this.syncedBuffers.get(e.document.uri);
if (syncedBuffer) {
syncedBuffer.onContentChanged(e.contentChanges);
}
let syncedBuffer = this.syncedBuffers.get(filepath);
if (!syncedBuffer) {
return;
}
syncedBuffer.onContentChanged(e.contentChanges);
}
public requestAllDiagnostics() {
if (!this._validate) {
return;
}
for (const filePath of this.syncedBuffers.keys()) {
for (const filePath of this.syncedBuffers.allResources) {
this.pendingDiagnostics.set(filePath, Date.now());
}
this.diagnosticDelayer.trigger(() => {
@ -213,16 +245,21 @@ export default class BufferSyncSupport {
}, 200);
}
public requestDiagnostic(file: string): void {
public requestDiagnostic(resource: Uri): void {
if (!this._validate) {
return;
}
const file = this.client.normalizePath(resource);
if (!file) {
return;
}
this.pendingDiagnostics.set(file, Date.now());
const buffer = this.syncedBuffers.get(file);
const buffer = this.syncedBuffers.get(resource);
let delay = 300;
if (buffer) {
let lineCount = buffer.lineCount;
const lineCount = buffer.lineCount;
delay = Math.min(Math.max(Math.ceil(lineCount / 20), 300), 800);
}
this.diagnosticDelayer.trigger(() => {
@ -246,7 +283,7 @@ export default class BufferSyncSupport {
});
// Add all open TS buffers to the geterr request. They might be visible
for (const file of this.syncedBuffers.keys()) {
for (const file of this.syncedBuffers.allResources) {
if (!this.pendingDiagnostics.get(file)) {
files.push(file);
}

View file

@ -70,7 +70,7 @@ class MyCompletionItem extends vscode.CompletionItem {
}
}
if (tsEntry.kindModifiers.match(/\boptional\b/)) {
if (tsEntry.kindModifiers && tsEntry.kindModifiers.match(/\boptional\b/)) {
this.insertText = this.label;
this.filterText = this.label;
this.label += '?';

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { Diagnostic, DiagnosticCollection, languages, Uri } from 'vscode';
import { ITypeScriptServiceClient } from '../typescriptService';
class DiagnosticSet {
private _map: ObjectMap<Diagnostic[]> = Object.create(null);
@ -39,8 +38,7 @@ export default class DiagnosticsManager {
private _validate: boolean = true;
constructor(
language: string,
private readonly client: ITypeScriptServiceClient
language: string
) {
this.syntaxDiagnostics = new DiagnosticSet();
this.semanticDiagnostics = new DiagnosticSet();
@ -81,8 +79,8 @@ export default class DiagnosticsManager {
this.currentDiagnostics.set(file, diagnostics);
}
public delete(file: string): void {
this.currentDiagnostics.delete(this.client.asUrl(file));
public delete(resource: Uri): void {
this.currentDiagnostics.delete(resource);
}
private updateCurrentDiagnostics(file: Uri) {

View file

@ -75,28 +75,30 @@ export class TypeScriptFormattingProvider implements DocumentRangeFormattingEdit
if (!filepath) {
return [];
}
await this.formattingOptionsManager.ensureFormatOptions(document, options, token);
const args: Proto.FormatOnKeyRequestArgs = {
file: filepath,
line: position.line + 1,
offset: position.character + 1,
key: ch
};
await this.formattingOptionsManager.ensureFormatOptions(document, options, token);
return this.client.execute('formatonkey', args, token).then((response): TextEdit[] => {
let edits = response.body;
let result: TextEdit[] = [];
try {
const response = await this.client.execute('formatonkey', args, token);
const edits = response.body;
const result: TextEdit[] = [];
if (!edits) {
return result;
}
for (const edit of edits) {
let textEdit = this.codeEdit2SingleEditOperation(edit);
let range = textEdit.range;
const textEdit = this.codeEdit2SingleEditOperation(edit);
const range = textEdit.range;
// Work around for https://github.com/Microsoft/TypeScript/issues/6700.
// Check if we have an edit at the beginning of the line which only removes white spaces and leaves
// an empty line. Drop those edits
if (range.start.character === 0 && range.start.line === range.end.line && textEdit.newText === '') {
let lText = document.lineAt(range.start.line).text;
const lText = document.lineAt(range.start.line).text;
// If the edit leaves something on the line keep the edit (note that the end character is exclusive).
// Keep it also if it removes something else than whitespace
if (lText.trim().length > 0 || lText.length > range.end.character) {
@ -107,9 +109,10 @@ export class TypeScriptFormattingProvider implements DocumentRangeFormattingEdit
}
}
return result;
}, () => {
return [];
});
} catch {
// noop
}
return [];
}
private codeEdit2SingleEditOperation(edit: Proto.CodeEdit): TextEdit {

View file

@ -186,7 +186,7 @@ class TscTaskProvider implements vscode.TaskProvider {
project.workspaceFolder || vscode.TaskScope.Workspace,
localize('buildTscLabel', 'build - {0}', label),
'tsc',
new vscode.ShellExecution(`${command} -p "${project.path}"`),
new vscode.ShellExecution(command, ['-p', project.path]),
'$tsc');
buildTask.group = vscode.TaskGroup.Build;
buildTask.isBackground = false;
@ -200,7 +200,7 @@ class TscTaskProvider implements vscode.TaskProvider {
project.workspaceFolder || vscode.TaskScope.Workspace,
localize('buildAndWatchTscLabel', 'watch - {0}', label),
'tsc',
new vscode.ShellExecution(`${command} --watch -p "${project.path}"`),
new vscode.ShellExecution(command, ['--watch', '-p', project.path]),
'$tsc-watch');
watchTask.group = vscode.TaskGroup.Build;
watchTask.isBackground = true;

View file

@ -46,12 +46,12 @@ export default class LanguageProvider {
) {
this.formattingOptionsManager = new FormattingConfigurationManager(client);
this.bufferSyncSupport = new BufferSyncSupport(client, description.modeIds, {
delete: (file: string) => {
this.diagnosticsManager.delete(file);
delete: (resource) => {
this.diagnosticsManager.delete(resource);
}
}, this._validate);
this.diagnosticsManager = new DiagnosticsManager(description.id, this.client);
this.diagnosticsManager = new DiagnosticsManager(description.id);
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables);
this.configurationChanged();

View file

@ -128,7 +128,7 @@ class ForkedTsServerProcess {
public createReader(
callback: ICallback<Proto.Response>,
onError: (error: any) => void = () => ({})
onError: (error: any) => void
) {
// tslint:disable-next-line:no-unused-expression
new Reader<Proto.Response>(this.childProcess.stdout, callback, onError);

View file

@ -8,40 +8,40 @@ import { TypeScriptVersion } from './versionProvider';
import * as languageModeIds from './languageModeIds';
export default class VersionStatus {
private readonly onChangeEditorSub: vscode.Disposable;
private readonly versionBarEntry: vscode.StatusBarItem;
private readonly _onChangeEditorSub: vscode.Disposable;
private readonly _versionBarEntry: vscode.StatusBarItem;
constructor(
private readonly normalizePath: (resource: vscode.Uri) => string | null
private readonly _normalizePath: (resource: vscode.Uri) => string | null
) {
this.versionBarEntry = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 99 /* to the right of editor status (100) */);
this.onChangeEditorSub = vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this);
this._versionBarEntry = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 99 /* to the right of editor status (100) */);
this._onChangeEditorSub = vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this);
}
dispose() {
this.versionBarEntry.dispose();
this.onChangeEditorSub.dispose();
this._versionBarEntry.dispose();
this._onChangeEditorSub.dispose();
}
public onDidChangeTypeScriptVersion(version: TypeScriptVersion) {
this.showHideStatus();
this.versionBarEntry.text = version.versionString;
this.versionBarEntry.tooltip = version.path;
this.versionBarEntry.command = 'typescript.selectTypeScriptVersion';
this._versionBarEntry.text = version.versionString;
this._versionBarEntry.tooltip = version.path;
this._versionBarEntry.command = 'typescript.selectTypeScriptVersion';
}
private showHideStatus() {
if (!vscode.window.activeTextEditor) {
this.versionBarEntry.hide();
this._versionBarEntry.hide();
return;
}
const doc = vscode.window.activeTextEditor.document;
if (vscode.languages.match([languageModeIds.typescript, languageModeIds.typescriptreact], doc)) {
if (this.normalizePath(doc.uri)) {
this.versionBarEntry.show();
if (this._normalizePath(doc.uri)) {
this._versionBarEntry.show();
} else {
this.versionBarEntry.hide();
this._versionBarEntry.hide();
}
return;
}
@ -52,6 +52,6 @@ export default class VersionStatus {
return;
}
this.versionBarEntry.hide();
this._versionBarEntry.hide();
}
}

View file

@ -90,7 +90,7 @@ export class Reader<T> {
public constructor(
private readonly readable: stream.Readable,
private readonly callback: ICallback<T>,
private readonly onError: (error: any) => void = () => ({})
private readonly onError: (error: any) => void
) {
this.readable.on('data', (data: Buffer) => {
this.onLengthData(data);

View file

@ -44,9 +44,9 @@
"vscode-chokidar": "1.6.2",
"vscode-debugprotocol": "1.27.0",
"vscode-nsfw": "1.0.17",
"vscode-ripgrep": "0.7.1-patch.0.1",
"vscode-ripgrep": "^0.8.1",
"vscode-textmate": "^3.2.0",
"vscode-xterm": "3.3.0-beta4",
"vscode-xterm": "3.3.0-beta5",
"yauzl": "2.8.0"
},
"devDependencies": {

View file

@ -10,6 +10,8 @@ perf.mark('main:started');
// Perf measurements
global.perfStartTime = Date.now();
Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
//#region Add support for using node_modules.asar
(function () {
const path = require('path');
@ -280,7 +282,7 @@ function getNLSConfiguration(locale) {
let isCoreLangaguage = true;
if (locale) {
isCoreLangaguage = ['de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'tr', 'zh-cn', 'zh-tw'].some((language) => {
isCoreLangaguage = ['de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-cn', 'zh-tw'].some((language) => {
return locale === language || locale.startsWith(language + '-');
});
}
@ -366,7 +368,7 @@ function getNLSConfiguration(locale) {
}
target[module] = targetStrings;
}
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g,'!') + '.nls.json'), JSON.stringify(target)));
writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target)));
}
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
return Promise.all(writes);

View file

@ -571,11 +571,12 @@ declare module 'vscode-xterm' {
* @param addon The addon to apply.
*/
static applyAddon(addon: any): void;
}
}
// Modifications to official .d.ts below
// Modifications to official .d.ts below
declare module 'vscode-xterm' {
interface Terminal {
buffer: {
/**
* The viewport position.
@ -606,6 +607,6 @@ declare module 'vscode-xterm' {
webLinksInit(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void;
winptyCompatInit(): void;
charMeasure?: { height: number, width: number }
charMeasure?: { height: number, width: number };
}
}
}

View file

@ -459,6 +459,9 @@ const sizeUtils = {
getBorderLeftWidth: function (element: HTMLElement): number {
return getDimension(element, 'border-left-width', 'borderLeftWidth');
},
getBorderRightWidth: function (element: HTMLElement): number {
return getDimension(element, 'border-right-width', 'borderRightWidth');
},
getBorderTopWidth: function (element: HTMLElement): number {
return getDimension(element, 'border-top-width', 'borderTopWidth');
},
@ -466,6 +469,12 @@ const sizeUtils = {
return getDimension(element, 'border-bottom-width', 'borderBottomWidth');
},
getPaddingLeft: function (element: HTMLElement): number {
return getDimension(element, 'padding-left', 'paddingLeft');
},
getPaddingRight: function (element: HTMLElement): number {
return getDimension(element, 'padding-right', 'paddingRight');
},
getPaddingTop: function (element: HTMLElement): number {
return getDimension(element, 'padding-top', 'paddingTop');
},
@ -571,6 +580,12 @@ export function getTotalWidth(element: HTMLElement): number {
return element.offsetWidth + margin;
}
export function getContentWidth(element: HTMLElement): number {
let border = sizeUtils.getBorderLeftWidth(element) + sizeUtils.getBorderRightWidth(element);
let padding = sizeUtils.getPaddingLeft(element) + sizeUtils.getPaddingRight(element);
return element.offsetWidth - border - padding;
}
export function getTotalScrollWidth(element: HTMLElement): number {
let margin = sizeUtils.getMarginLeft(element) + sizeUtils.getMarginRight(element);
return element.scrollWidth + margin;

View file

@ -580,6 +580,89 @@ export interface IOpenController {
shouldOpen(event: UIEvent): boolean;
}
export interface IStyleController {
style(styles: IListStyles): void;
}
export class DefaultStyleController implements IStyleController {
constructor(private styleElement: HTMLStyleElement, private selectorSuffix?: string) { }
style(styles: IListStyles): void {
const suffix = this.selectorSuffix ? `.${this.selectorSuffix}` : '';
const content: string[] = [];
if (styles.listFocusBackground) {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { background-color: ${styles.listFocusBackground}; }`);
content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused:hover { background-color: ${styles.listFocusBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listFocusForeground) {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { color: ${styles.listFocusForeground}; }`);
}
if (styles.listActiveSelectionBackground) {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected { background-color: ${styles.listActiveSelectionBackground}; }`);
content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listActiveSelectionForeground) {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected { color: ${styles.listActiveSelectionForeground}; }`);
}
if (styles.listFocusAndSelectionBackground) {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; }`);
}
if (styles.listFocusAndSelectionForeground) {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; }`);
}
if (styles.listInactiveFocusBackground) {
content.push(`.monaco-list${suffix} .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`);
content.push(`.monaco-list${suffix} .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listInactiveSelectionBackground) {
content.push(`.monaco-list${suffix} .monaco-list-row.selected { background-color: ${styles.listInactiveSelectionBackground}; }`);
content.push(`.monaco-list${suffix} .monaco-list-row.selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listInactiveSelectionForeground) {
content.push(`.monaco-list${suffix} .monaco-list-row.selected { color: ${styles.listInactiveSelectionForeground}; }`);
}
if (styles.listHoverBackground) {
content.push(`.monaco-list${suffix} .monaco-list-row:hover { background-color: ${styles.listHoverBackground}; }`);
}
if (styles.listHoverForeground) {
content.push(`.monaco-list${suffix} .monaco-list-row:hover { color: ${styles.listHoverForeground}; }`);
}
if (styles.listSelectionOutline) {
content.push(`.monaco-list${suffix} .monaco-list-row.selected { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`);
}
if (styles.listFocusOutline) {
content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`);
}
if (styles.listInactiveFocusOutline) {
content.push(`.monaco-list${suffix} .monaco-list-row.focused { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`);
}
if (styles.listHoverOutline) {
content.push(`.monaco-list${suffix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
}
const newStyles = content.join('\n');
if (newStyles !== this.styleElement.innerHTML) {
this.styleElement.innerHTML = newStyles;
}
}
}
export interface IListOptions<T> extends IListViewOptions, IListStyles {
identityProvider?: IIdentityProvider<T>;
ariaLabel?: string;
@ -591,6 +674,7 @@ export interface IListOptions<T> extends IListViewOptions, IListStyles {
multipleSelectionSupport?: boolean;
multipleSelectionController?: IMultipleSelectionController<T>;
openController?: IOpenController;
styleController?: IStyleController;
}
export interface IListStyles {
@ -754,6 +838,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
private spliceable: ISpliceable<T>;
protected disposables: IDisposable[];
private styleElement: HTMLStyleElement;
private styleController: IStyleController;
private mouseController: MouseController<T>;
@memoize get onFocusChange(): Event<IListEvent<T>> {
@ -816,6 +901,11 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.styleElement = DOM.createStyleSheet(this.view.domNode);
this.styleController = options.styleController;
if (!this.styleController) {
this.styleController = new DefaultStyleController(this.styleElement, this.idPrefix);
}
this.spliceable = new CombinedSpliceable([
new TraitSpliceable(this.focus, this.view, options.identityProvider),
new TraitSpliceable(this.selection, this.view, options.identityProvider),
@ -1087,73 +1177,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
}
style(styles: IListStyles): void {
const content: string[] = [];
if (styles.listFocusBackground) {
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.focused { background-color: ${styles.listFocusBackground}; }`);
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.focused:hover { background-color: ${styles.listFocusBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listFocusForeground) {
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.focused { color: ${styles.listFocusForeground}; }`);
}
if (styles.listActiveSelectionBackground) {
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected { background-color: ${styles.listActiveSelectionBackground}; }`);
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listActiveSelectionForeground) {
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected { color: ${styles.listActiveSelectionForeground}; }`);
}
if (styles.listFocusAndSelectionBackground) {
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; }`);
}
if (styles.listFocusAndSelectionForeground) {
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; }`);
}
if (styles.listInactiveFocusBackground) {
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`);
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listInactiveSelectionBackground) {
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.selected { background-color: ${styles.listInactiveSelectionBackground}; }`);
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listInactiveSelectionForeground) {
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.selected { color: ${styles.listInactiveSelectionForeground}; }`);
}
if (styles.listHoverBackground) {
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row:hover { background-color: ${styles.listHoverBackground}; }`);
}
if (styles.listHoverForeground) {
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row:hover { color: ${styles.listHoverForeground}; }`);
}
if (styles.listSelectionOutline) {
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.selected { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`);
}
if (styles.listFocusOutline) {
content.push(`.monaco-list.${this.idPrefix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`);
}
if (styles.listInactiveFocusOutline) {
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row.focused { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`);
}
if (styles.listHoverOutline) {
content.push(`.monaco-list.${this.idPrefix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
}
this.styleElement.innerHTML = content.join('\n');
this.styleController.style(styles);
}
private toListEvent({ indexes }: ITraitChangeEvent) {

View file

@ -2,9 +2,11 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-progress-container {
width: 100%;
height: 5px;
overflow: hidden; /* keep progress bit in bounds */
}
.monaco-progress-container .progress-bit {
@ -49,9 +51,17 @@
-moz-animation-duration: 4s;
-moz-animation-iteration-count: infinite;
-moz-animation-timing-function: linear;
will-change: transform;
}
@keyframes progress { from { left: 0; width: 2%; } 50% { left: 50%; width: 5%; } to { left: 98%; width: 2%; } }
@-ms-keyframes progress { from { left: 0; width: 2%; } 50% { left: 50%; width: 5%; } to { left: 98%; width: 2%; } }
@-webkit-keyframes progress { from { left: 0; width: 2%; } 50% { left: 50%; width: 5%; } to { left: 98%; width: 2%; } }
@-moz-keyframes progress { from { left: 0; width: 2%; } 50% { left: 50%; width: 5%; } to { left: 98%; width: 2%; } }
/**
* The progress bit has a width: 2% (1/50) of the parent container. The animation moves it from 0% to 100% of
* that container. Since translateX is relative to the progress bit size, we have to multiple it with
* its relative size to the parent container:
* 50%: 50 * 50 = 2500%
* 100%: 50 * 100 - 50 (do not overflow): 4950%
*/
@keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4950%) scaleX(1) } }
@-ms-keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4950%) scaleX(1) } }
@-webkit-keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4950%) scaleX(1) } }
@-moz-keyframes progress { from { transform: translateX(0%) scaleX(1) } 50% { transform: translateX(2500%) scaleX(3) } to { transform: translateX(4950%) scaleX(1) } }

View file

@ -240,6 +240,7 @@ export class QuickOpenWidget implements IModelProvider {
indentPixels: 0,
alwaysFocused: true,
verticalScrollMode: ScrollbarVisibility.Visible,
horizontalScrollMode: ScrollbarVisibility.Hidden,
ariaLabel: nls.localize('treeAriaLabel', "Quick Picker"),
keyboardSupport: this.options.keyboardSupport,
preventRootFocus: true
@ -810,11 +811,13 @@ export class QuickOpenWidget implements IModelProvider {
}
}
public setValue(value: string, selection?: [number, number]): void {
public setValue(value: string, selectionOrStableHint?: [number, number] | null): void {
if (this.inputBox) {
this.inputBox.value = value;
if (Array.isArray(selection)) {
const [start, end] = selection;
if (selectionOrStableHint === null) {
// null means stable-selection
} else if (Array.isArray(selectionOrStableHint)) {
const [start, end] = selectionOrStableHint;
this.inputBox.select({ start, end });
} else {
this.inputBox.select();

View file

@ -78,6 +78,11 @@ export interface ITree {
*/
refresh(element?: any, recursive?: boolean): WinJS.Promise;
/**
* Updates an element's width.
*/
updateWidth(element: any): void;
/**
* Expands an element.
* The returned promise returns a boolean for whether the element was expanded or not.
@ -666,6 +671,7 @@ export interface ITreeConfiguration {
filter?: IFilter;
sorter?: ISorter;
accessibilityProvider?: IAccessibilityProvider;
styler?: ITreeStyler;
}
export interface ITreeOptions extends ITreeStyles {
@ -673,6 +679,7 @@ export interface ITreeOptions extends ITreeStyles {
showTwistie?: boolean;
indentPixels?: number;
verticalScrollMode?: ScrollbarVisibility;
horizontalScrollMode?: ScrollbarVisibility;
alwaysFocused?: boolean;
autoExpandSingleChildren?: boolean;
useShadows?: boolean;
@ -682,6 +689,10 @@ export interface ITreeOptions extends ITreeStyles {
preventRootFocus?: boolean;
}
export interface ITreeStyler {
style(styles: ITreeStyles): void;
}
export interface ITreeStyles {
listFocusBackground?: Color;
listFocusForeground?: Color;

View file

@ -444,6 +444,87 @@ export class DefaultAccessibilityProvider implements _.IAccessibilityProvider {
}
}
export class DefaultTreestyler implements _.ITreeStyler {
constructor(private styleElement: HTMLStyleElement, private selectorSuffix?: string) { }
style(styles: _.ITreeStyles): void {
const suffix = this.selectorSuffix ? `.${this.selectorSuffix}` : '';
const content: string[] = [];
if (styles.listFocusBackground) {
content.push(`.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { background-color: ${styles.listFocusBackground}; }`);
}
if (styles.listFocusForeground) {
content.push(`.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { color: ${styles.listFocusForeground}; }`);
}
if (styles.listActiveSelectionBackground) {
content.push(`.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${styles.listActiveSelectionBackground}; }`);
}
if (styles.listActiveSelectionForeground) {
content.push(`.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${styles.listActiveSelectionForeground}; }`);
}
if (styles.listFocusAndSelectionBackground) {
content.push(`
.monaco-tree-drag-image,
.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { background-color: ${styles.listFocusAndSelectionBackground}; }
`);
}
if (styles.listFocusAndSelectionForeground) {
content.push(`
.monaco-tree-drag-image,
.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { color: ${styles.listFocusAndSelectionForeground}; }
`);
}
if (styles.listInactiveSelectionBackground) {
content.push(`.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${styles.listInactiveSelectionBackground}; }`);
}
if (styles.listInactiveSelectionForeground) {
content.push(`.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${styles.listInactiveSelectionForeground}; }`);
}
if (styles.listHoverBackground) {
content.push(`.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`);
}
if (styles.listHoverForeground) {
content.push(`.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`);
}
if (styles.listDropBackground) {
content.push(`
.monaco-tree${suffix} .monaco-tree-wrapper.drop-target,
.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; }
`);
}
if (styles.listFocusOutline) {
content.push(`
.monaco-tree-drag-image { border: 1px solid ${styles.listFocusOutline}; background: #000; }
.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row { border: 1px solid transparent; }
.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { border: 1px dotted ${styles.listFocusOutline}; }
.monaco-tree${suffix}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { border: 1px solid ${styles.listFocusOutline}; }
.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { border: 1px solid ${styles.listFocusOutline}; }
.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { border: 1px dashed ${styles.listFocusOutline}; }
.monaco-tree${suffix} .monaco-tree-wrapper.drop-target,
.monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.drop-target { border: 1px dashed ${styles.listFocusOutline}; }
`);
}
const newStyles = content.join('\n');
if (newStyles !== this.styleElement.innerHTML) {
this.styleElement.innerHTML = newStyles;
}
}
}
export class CollapseAllAction extends Action {
constructor(private viewer: _.ITree, enabled: boolean) {

View file

@ -30,6 +30,7 @@ export class TreeContext implements _.ITreeContext {
public filter: _.IFilter;
public sorter: _.ISorter;
public accessibilityProvider: _.IAccessibilityProvider;
public styler: _.ITreeStyler;
constructor(tree: _.ITree, configuration: _.ITreeConfiguration, options: _.ITreeOptions = {}) {
this.tree = tree;
@ -47,6 +48,7 @@ export class TreeContext implements _.ITreeContext {
this.filter = configuration.filter || new TreeDefaults.DefaultFilter();
this.sorter = configuration.sorter || null;
this.accessibilityProvider = configuration.accessibilityProvider || new TreeDefaults.DefaultAccessibilityProvider();
this.styler = configuration.styler || null;
}
}
@ -158,6 +160,11 @@ export class Tree implements _.ITree {
return this.model.refresh(element, recursive);
}
public updateWidth(element: any): void {
let item = this.model.getItem(element);
return this.view.updateWidth(item);
}
public expand(element: any): WinJS.Promise {
return this.model.expand(element);
}
@ -212,7 +219,7 @@ export class Tree implements _.ITree {
}
getContentHeight(): number {
return this.view.getTotalHeight();
return this.view.getContentHeight();
}
public setHighlight(element?: any, eventPayload?: any): void {

View file

@ -25,6 +25,8 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import Event, { Emitter } from 'vs/base/common/event';
import { IDomNodePagePosition } from 'vs/base/browser/dom';
import { DataTransfers } from 'vs/base/browser/dnd';
import { DefaultTreestyler } from './treeDefaults';
import { Delayer } from 'vs/base/common/async';
export interface IRow {
element: HTMLElement;
@ -100,6 +102,7 @@ export class RowCache implements Lifecycle.IDisposable {
export interface IViewContext extends _.ITreeContext {
cache: RowCache;
horizontalScrolling: boolean;
}
export class ViewItem implements IViewItem {
@ -112,6 +115,7 @@ export class ViewItem implements IViewItem {
public top: number;
public height: number;
public width: number = 0;
public onDragStart: (e: DragEvent) => void;
public needsRender: boolean;
@ -250,10 +254,34 @@ export class ViewItem implements IViewItem {
}
if (!skipUserRender) {
const style = window.getComputedStyle(this.element);
const paddingLeft = parseFloat(style.paddingLeft);
if (this.context.horizontalScrolling) {
this.element.style.width = 'fit-content';
}
this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData);
if (this.context.horizontalScrolling) {
this.width = DOM.getContentWidth(this.element) + paddingLeft;
this.element.style.width = '';
}
}
}
updateWidth(): any {
if (!this.context.horizontalScrolling) {
return;
}
const style = window.getComputedStyle(this.element);
const paddingLeft = parseFloat(style.paddingLeft);
this.element.style.width = 'fit-content';
this.width = DOM.getContentWidth(this.element) + paddingLeft;
this.element.style.width = '';
}
public insertInDOM(container: HTMLElement, afterElement: HTMLElement): void {
if (!this.row) {
this.row = this.context.cache.alloc(this.templateId);
@ -378,12 +406,16 @@ export class TreeView extends HeightMap {
private domNode: HTMLElement;
private wrapper: HTMLElement;
private styleElement: HTMLStyleElement;
private treeStyler: _.ITreeStyler;
private rowsContainer: HTMLElement;
private scrollableElement: ScrollableElement;
private msGesture: MSGesture;
private lastPointerType: string;
private lastClickTimeStamp: number = 0;
private horizontalScrolling: boolean = true;
private contentWidthUpdateDelayer = new Delayer<void>(50);
private lastRenderTop: number;
private lastRenderHeight: number;
@ -420,6 +452,9 @@ export class TreeView extends HeightMap {
TreeView.counter++;
this.instance = TreeView.counter;
const horizontalScrollMode = typeof context.options.horizontalScrollMode === 'undefined' ? ScrollbarVisibility.Hidden : context.options.horizontalScrollMode;
const horizontalScrolling = horizontalScrollMode !== ScrollbarVisibility.Hidden;
this.context = {
dataSource: context.dataSource,
renderer: context.renderer,
@ -430,7 +465,8 @@ export class TreeView extends HeightMap {
tree: context.tree,
accessibilityProvider: context.accessibilityProvider,
options: context.options,
cache: new RowCache(context)
cache: new RowCache(context),
horizontalScrolling
};
this.modelListeners = [];
@ -446,6 +482,11 @@ export class TreeView extends HeightMap {
this.styleElement = DOM.createStyleSheet(this.domNode);
this.treeStyler = context.styler;
if (!this.treeStyler) {
this.treeStyler = new DefaultTreestyler(this.styleElement, `monaco-tree-instance-${this.instance}`);
}
// ARIA
this.domNode.setAttribute('role', 'tree');
if (this.context.options.ariaLabel) {
@ -464,12 +505,12 @@ export class TreeView extends HeightMap {
this.wrapper.className = 'monaco-tree-wrapper';
this.scrollableElement = new ScrollableElement(this.wrapper, {
alwaysConsumeMouseWheel: true,
horizontal: ScrollbarVisibility.Hidden,
horizontal: horizontalScrollMode,
vertical: (typeof context.options.verticalScrollMode !== 'undefined' ? context.options.verticalScrollMode : ScrollbarVisibility.Auto),
useShadows: context.options.useShadows
});
this.scrollableElement.onScroll((e) => {
this.render(e.scrollTop, e.height);
this.render(e.scrollTop, e.height, e.scrollLeft, e.width, e.scrollWidth);
});
if (Browser.isIE) {
@ -552,75 +593,7 @@ export class TreeView extends HeightMap {
}
public applyStyles(styles: _.ITreeStyles): void {
const content: string[] = [];
if (styles.listFocusBackground) {
content.push(`.monaco-tree.monaco-tree-instance-${this.instance}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { background-color: ${styles.listFocusBackground}; }`);
}
if (styles.listFocusForeground) {
content.push(`.monaco-tree.monaco-tree-instance-${this.instance}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { color: ${styles.listFocusForeground}; }`);
}
if (styles.listActiveSelectionBackground) {
content.push(`.monaco-tree.monaco-tree-instance-${this.instance}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${styles.listActiveSelectionBackground}; }`);
}
if (styles.listActiveSelectionForeground) {
content.push(`.monaco-tree.monaco-tree-instance-${this.instance}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${styles.listActiveSelectionForeground}; }`);
}
if (styles.listFocusAndSelectionBackground) {
content.push(`
.monaco-tree-drag-image,
.monaco-tree.monaco-tree-instance-${this.instance}.focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { background-color: ${styles.listFocusAndSelectionBackground}; }
`);
}
if (styles.listFocusAndSelectionForeground) {
content.push(`
.monaco-tree-drag-image,
.monaco-tree.monaco-tree-instance-${this.instance}.focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { color: ${styles.listFocusAndSelectionForeground}; }
`);
}
if (styles.listInactiveSelectionBackground) {
content.push(`.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: ${styles.listInactiveSelectionBackground}; }`);
}
if (styles.listInactiveSelectionForeground) {
content.push(`.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: ${styles.listInactiveSelectionForeground}; }`);
}
if (styles.listHoverBackground) {
content.push(`.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`);
}
if (styles.listHoverForeground) {
content.push(`.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`);
}
if (styles.listDropBackground) {
content.push(`
.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-wrapper.drop-target,
.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; }
`);
}
if (styles.listFocusOutline) {
content.push(`
.monaco-tree-drag-image { border: 1px solid ${styles.listFocusOutline}; background: #000; }
.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row { border: 1px solid transparent; }
.monaco-tree.monaco-tree-instance-${this.instance}.focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { border: 1px dotted ${styles.listFocusOutline}; }
.monaco-tree.monaco-tree-instance-${this.instance}.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { border: 1px solid ${styles.listFocusOutline}; }
.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { border: 1px solid ${styles.listFocusOutline}; }
.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { border: 1px dashed ${styles.listFocusOutline}; }
.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-wrapper.drop-target,
.monaco-tree.monaco-tree-instance-${this.instance} .monaco-tree-rows > .monaco-tree-row.drop-target { border: 1px dashed ${styles.listFocusOutline}; }
`);
}
this.styleElement.innerHTML = content.join('\n');
this.treeStyler.style(styles);
}
protected createViewItem(item: Model.Item): IViewItem {
@ -670,9 +643,14 @@ export class TreeView extends HeightMap {
}
this.viewHeight = height || DOM.getContentHeight(this.wrapper); // render
this.scrollHeight = this.getContentHeight();
if (this.horizontalScrolling) {
this.viewWidth = DOM.getContentWidth(this.wrapper);
}
}
private render(scrollTop: number, viewHeight: number): void {
private render(scrollTop: number, viewHeight: number, scrollLeft: number, viewWidth: number, scrollWidth: number): void {
var i: number;
var stop: number;
@ -706,6 +684,11 @@ export class TreeView extends HeightMap {
this.rowsContainer.style.top = (topItem.top - renderTop) + 'px';
}
if (this.horizontalScrolling) {
this.rowsContainer.style.left = -scrollLeft + 'px';
this.rowsContainer.style.width = `${Math.max(scrollWidth, viewWidth)}px`;
}
this.lastRenderTop = renderTop;
this.lastRenderHeight = renderBottom - renderTop;
}
@ -746,6 +729,24 @@ export class TreeView extends HeightMap {
}
this.scrollTop = scrollTop;
this.updateScrollWidth();
}
private updateScrollWidth(): void {
if (!this.horizontalScrolling) {
return;
}
this.contentWidthUpdateDelayer.trigger(() => {
const keys = Object.keys(this.items);
let scrollWidth = 0;
for (const key of keys) {
scrollWidth = Math.max(scrollWidth, this.items[key].width);
}
this.scrollWidth = scrollWidth + 10 /* scrollbar */;
});
}
public focusNextPage(eventPayload?: any): void {
@ -803,11 +804,25 @@ export class TreeView extends HeightMap {
return scrollDimensions.height;
}
public set viewHeight(viewHeight: number) {
this.scrollableElement.setScrollDimensions({
height: viewHeight,
scrollHeight: this.getTotalHeight()
});
public set viewHeight(height: number) {
this.scrollableElement.setScrollDimensions({ height });
}
private set scrollHeight(scrollHeight: number) {
this.scrollableElement.setScrollDimensions({ scrollHeight });
}
public get viewWidth(): number {
const scrollDimensions = this.scrollableElement.getScrollDimensions();
return scrollDimensions.width;
}
public set viewWidth(viewWidth: number) {
this.scrollableElement.setScrollDimensions({ width: viewWidth });
}
private set scrollWidth(scrollWidth: number) {
this.scrollableElement.setScrollDimensions({ scrollWidth });
}
public get scrollTop(): number {
@ -817,7 +832,7 @@ export class TreeView extends HeightMap {
public set scrollTop(scrollTop: number) {
this.scrollableElement.setScrollDimensions({
scrollHeight: this.getTotalHeight()
scrollHeight: this.getContentHeight()
});
this.scrollableElement.setScrollPosition({
scrollTop: scrollTop
@ -825,12 +840,12 @@ export class TreeView extends HeightMap {
}
public getScrollPosition(): number {
const height = this.getTotalHeight() - this.viewHeight;
const height = this.getContentHeight() - this.viewHeight;
return height <= 0 ? 1 : this.scrollTop / height;
}
public setScrollPosition(pos: number): void {
const height = this.getTotalHeight() - this.viewHeight;
const height = this.getContentHeight() - this.viewHeight;
this.scrollTop = height * pos;
}
@ -999,6 +1014,21 @@ export class TreeView extends HeightMap {
}
}
public updateWidth(item: Model.Item): void {
if (!item || !item.isVisible()) {
return;
}
const viewItem = this.items[item.id];
if (!viewItem) {
return;
}
viewItem.updateWidth();
this.updateScrollWidth();
}
public getRelativeTop(item: Model.Item): number {
if (item && item.isVisible()) {
var viewItem = this.items[item.id];

View file

@ -10,6 +10,7 @@ export interface IViewItem {
model: Item;
top: number;
height: number;
width: number;
}
export class HeightMap {
@ -22,7 +23,7 @@ export class HeightMap {
this.indexes = {};
}
public getTotalHeight(): number {
public getContentHeight(): number {
var last = this.heightMap[this.heightMap.length - 1];
return !last ? 0 : last.top + last.height;
}

View file

@ -45,7 +45,8 @@ class TestHeightMap extends HeightMap {
return {
model: item,
top: 0,
height: item.getHeight()
height: item.getHeight(),
width: 0
};
}
}

View file

@ -645,8 +645,8 @@ export class IssueReporter extends Disposable {
/* __GDPR__
"issueReporterSubmit" : {
"issueType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"numSimilarIssuesDisplayed" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"issueType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"numSimilarIssuesDisplayed" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('issueReporterSubmit', { issueType: this.issueReporterModel.getData().issueType, numSimilarIssuesDisplayed: this.numberOfSearchResultsDisplayed });

View file

@ -878,7 +878,9 @@ export class CodeWindow implements ICodeWindow {
}
public send(channel: string, ...args: any[]): void {
this._win.webContents.send(channel, ...args);
if (this._win) {
this._win.webContents.send(channel, ...args);
}
}
public updateTouchBar(groups: ICommandAction[][]): void {

View file

@ -1536,6 +1536,7 @@ export class WindowsManager implements IWindowsMainService {
if (!internalOptions.telemetryEventName) {
if (pickFolders && pickFiles) {
// __GDPR__TODO__ classify event
internalOptions.telemetryEventName = 'openFileFolder';
} else if (pickFolders) {
internalOptions.telemetryEventName = 'openFolder';

View file

@ -123,11 +123,11 @@ export interface BufferCursor {
}
export class Piece {
bufferIndex: number;
start: BufferCursor;
end: BufferCursor;
length: number;
lineFeedCnt: number;
readonly bufferIndex: number;
readonly start: BufferCursor;
readonly end: BufferCursor;
readonly length: number;
readonly lineFeedCnt: number;
constructor(bufferIndex: number, start: BufferCursor, end: BufferCursor, lineFeedCnt: number, length: number) {
this.bufferIndex = bufferIndex;
@ -155,20 +155,20 @@ export class StringBuffer {
* 2. TreeNode/Buffers normalization should not happen during snapshot reading.
*/
class PieceTreeSnapshot implements ITextSnapshot {
private _nodes: TreeNode[]; // pieces/tree nodes in order
private _pieces: Piece[];
private _index: number;
private _tree: PieceTreeBase;
private _BOM: string;
constructor(tree: PieceTreeBase, BOM: string) {
this._nodes = [];
this._pieces = [];
this._tree = tree;
this._BOM = BOM;
this._index = 0;
if (tree.root !== SENTINEL) {
tree.iterate(tree.root, node => {
if (node !== SENTINEL) {
this._nodes.push(node);
this._pieces.push(node.piece);
}
return true;
});
@ -176,7 +176,7 @@ class PieceTreeSnapshot implements ITextSnapshot {
}
read(): string {
if (this._nodes.length === 0) {
if (this._pieces.length === 0) {
if (this._index === 0) {
this._index++;
return this._BOM;
@ -185,14 +185,14 @@ class PieceTreeSnapshot implements ITextSnapshot {
}
}
if (this._index > this._nodes.length - 1) {
if (this._index > this._pieces.length - 1) {
return null;
}
if (this._index === 0) {
return this._BOM + this._tree.getNodeContent(this._nodes[this._index++]);
return this._BOM + this._tree.getPieceContent(this._pieces[this._index++]);
}
return this._tree.getNodeContent(this._nodes[this._index++]);
return this._tree.getPieceContent(this._pieces[this._index++]);
}
}
@ -578,9 +578,14 @@ export class PieceTreeBase {
if (headOfRight === 10 /** \n */) {
let newStart: BufferCursor = { line: newRightPiece.start.line + 1, column: 0 };
newRightPiece.start = newStart;
newRightPiece.length -= 1;
newRightPiece.lineFeedCnt = this.getLineFeedCnt(newRightPiece.bufferIndex, newRightPiece.start, newRightPiece.end);
newRightPiece = new Piece(
newRightPiece.bufferIndex,
newStart,
newRightPiece.end,
this.getLineFeedCnt(newRightPiece.bufferIndex, newStart, newRightPiece.end),
newRightPiece.length - 1
);
value += '\n';
}
}
@ -703,9 +708,15 @@ export class PieceTreeBase {
let piece = node.piece;
let newStart: BufferCursor = { line: piece.start.line + 1, column: 0 };
piece.start = newStart;
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
piece.length -= 1;
let nPiece = new Piece(
piece.bufferIndex,
newStart,
piece.end,
this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end),
piece.length - 1
);
node.piece = nPiece;
value += '\n';
updateTreeMetadata(this, node, -1, -1);
@ -987,46 +998,72 @@ export class PieceTreeBase {
}
deleteNodeTail(node: TreeNode, pos: BufferCursor) {
let piece = node.piece;
let originalLFCnt = piece.lineFeedCnt;
let originalEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
piece.end = pos;
let newEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
let lf_delta = piece.lineFeedCnt - originalLFCnt;
let size_delta = newEndOffset - originalEndOffset;
piece.length += size_delta;
const piece = node.piece;
const originalLFCnt = piece.lineFeedCnt;
const originalEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
const newEnd = pos;
const newEndOffset = this.offsetInBuffer(piece.bufferIndex, newEnd);
const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, newEnd);
const lf_delta = newLineFeedCnt - originalLFCnt;
const size_delta = newEndOffset - originalEndOffset;
const newLength = piece.length + size_delta;
node.piece = new Piece(
piece.bufferIndex,
piece.start,
newEnd,
newLineFeedCnt,
newLength
);
updateTreeMetadata(this, node, size_delta, lf_delta);
}
deleteNodeHead(node: TreeNode, pos: BufferCursor) {
let piece = node.piece;
let originalLFCnt = piece.lineFeedCnt;
let originalStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
const piece = node.piece;
const originalLFCnt = piece.lineFeedCnt;
const originalStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
const newStart = pos;
const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end);
const newStartOffset = this.offsetInBuffer(piece.bufferIndex, newStart);
const lf_delta = newLineFeedCnt - originalLFCnt;
const size_delta = originalStartOffset - newStartOffset;
const newLength = piece.length + size_delta;
node.piece = new Piece(
piece.bufferIndex,
newStart,
piece.end,
newLineFeedCnt,
newLength
);
piece.start = pos;
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
let newStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
let lf_delta = piece.lineFeedCnt - originalLFCnt;
let size_delta = originalStartOffset - newStartOffset;
piece.length += size_delta;
updateTreeMetadata(this, node, size_delta, lf_delta);
}
shrinkNode(node: TreeNode, start: BufferCursor, end: BufferCursor) {
let piece = node.piece;
let originalStartPos = piece.start;
let originalEndPos = piece.end;
const piece = node.piece;
const originalStartPos = piece.start;
const originalEndPos = piece.end;
// old piece, originalStartPos, start
let oldLength = piece.length;
let oldLFCnt = piece.lineFeedCnt;
piece.end = start;
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
let newLength = this.offsetInBuffer(piece.bufferIndex, start) - this.offsetInBuffer(piece.bufferIndex, originalStartPos);
let newLFCnt = piece.lineFeedCnt;
piece.length = newLength;
updateTreeMetadata(this, node, newLength - oldLength, newLFCnt - oldLFCnt);
const oldLength = piece.length;
const oldLFCnt = piece.lineFeedCnt;
const newEnd = start;
const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, newEnd);
const newLength = this.offsetInBuffer(piece.bufferIndex, start) - this.offsetInBuffer(piece.bufferIndex, originalStartPos);
node.piece = new Piece(
piece.bufferIndex,
piece.start,
newEnd,
newLineFeedCnt,
newLength
);
updateTreeMetadata(this, node, newLength - oldLength, newLineFeedCnt - oldLFCnt);
// new right piece, end, originalEndPos
let newPiece = new Piece(
@ -1046,7 +1083,7 @@ export class PieceTreeBase {
value += '\n';
}
let hitCRLF = this.shouldCheckCRLF() && this.startWithLF(value) && this.endWithCR(node);
const hitCRLF = this.shouldCheckCRLF() && this.startWithLF(value) && this.endWithCR(node);
const startOffset = this._buffers[0].buffer.length;
this._buffers[0].buffer += value;
const lineStarts = createLineStartsFast(value, false);
@ -1061,16 +1098,23 @@ export class PieceTreeBase {
}
this._buffers[0].lineStarts = (<number[]>this._buffers[0].lineStarts).concat(<number[]>lineStarts.slice(1));
let endIndex = this._buffers[0].lineStarts.length - 1;
let endColumn = this._buffers[0].buffer.length - this._buffers[0].lineStarts[endIndex];
let endPos = { line: endIndex, column: endColumn };
node.piece.end = endPos;
node.piece.length += value.length;
let oldLineFeedCnt = node.piece.lineFeedCnt;
let newLineFeedCnt = this.getLineFeedCnt(0, node.piece.start, endPos);
node.piece.lineFeedCnt = newLineFeedCnt;
let lf_delta = newLineFeedCnt - oldLineFeedCnt;
this._lastChangeBufferPos = endPos;
const endIndex = this._buffers[0].lineStarts.length - 1;
const endColumn = this._buffers[0].buffer.length - this._buffers[0].lineStarts[endIndex];
const newEnd = { line: endIndex, column: endColumn };
const newLength = node.piece.length + value.length;
const oldLineFeedCnt = node.piece.lineFeedCnt;
const newLineFeedCnt = this.getLineFeedCnt(0, node.piece.start, newEnd);
const lf_delta = newLineFeedCnt - oldLineFeedCnt;
node.piece = new Piece(
node.piece.bufferIndex,
node.piece.start,
newEnd,
newLineFeedCnt,
newLength
);
this._lastChangeBufferPos = newEnd;
updateTreeMetadata(this, node, value.length, lf_delta);
}
@ -1266,18 +1310,24 @@ export class PieceTreeBase {
let nodesToDel = [];
// update node
let lineStarts = this._buffers[prev.piece.bufferIndex].lineStarts;
let newEnd: BufferCursor;
if (prev.piece.end.column === 0) {
// it means, last line ends with \r, not \r\n
let newEnd: BufferCursor = { line: prev.piece.end.line - 1, column: lineStarts[prev.piece.end.line] - lineStarts[prev.piece.end.line - 1] - 1 };
prev.piece.end = newEnd;
newEnd = { line: prev.piece.end.line - 1, column: lineStarts[prev.piece.end.line] - lineStarts[prev.piece.end.line - 1] - 1 };
} else {
// \r\n
let newEnd: BufferCursor = { line: prev.piece.end.line, column: prev.piece.end.column - 1 };
prev.piece.end = newEnd;
newEnd = { line: prev.piece.end.line, column: prev.piece.end.column - 1 };
}
prev.piece.length -= 1;
prev.piece.lineFeedCnt -= 1;
const prevNewLength = prev.piece.length - 1;
const prevNewLFCnt = prev.piece.lineFeedCnt - 1;
prev.piece = new Piece(
prev.piece.bufferIndex,
prev.piece.start,
newEnd,
prevNewLFCnt,
prevNewLength
);
updateTreeMetadata(this, prev, - 1, -1);
if (prev.piece.length === 0) {
@ -1286,10 +1336,15 @@ export class PieceTreeBase {
// update nextNode
let newStart: BufferCursor = { line: next.piece.start.line + 1, column: 0 };
next.piece.start = newStart;
next.piece.length -= 1;
next.piece.lineFeedCnt = this.getLineFeedCnt(next.piece.bufferIndex, next.piece.start, next.piece.end);
// }
const newLength = next.piece.length - 1;
const newLineFeedCnt = this.getLineFeedCnt(next.piece.bufferIndex, newStart, next.piece.end);
next.piece = new Piece(
next.piece.bufferIndex,
newStart,
next.piece.end,
newLineFeedCnt,
newLength
);
updateTreeMetadata(this, next, - 1, -1);
if (next.piece.length === 0) {
@ -1317,11 +1372,18 @@ export class PieceTreeBase {
rbDelete(this, nextNode);
} else {
let piece = nextNode.piece;
let newStart: BufferCursor = { line: piece.start.line + 1, column: 0 };
piece.start = newStart;
piece.length -= 1;
piece.lineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, piece.start, piece.end);
const piece = nextNode.piece;
const newStart: BufferCursor = { line: piece.start.line + 1, column: 0 };
const newLength = piece.length - 1;
const newLineFeedCnt = this.getLineFeedCnt(piece.bufferIndex, newStart, piece.end);
nextNode.piece = new Piece(
piece.bufferIndex,
newStart,
piece.end,
newLineFeedCnt,
newLength
);
updateTreeMetadata(this, nextNode, -1, -1);
}
return true;
@ -1362,6 +1424,14 @@ export class PieceTreeBase {
return currentContent;
}
getPieceContent(piece: Piece) {
let buffer = this._buffers[piece.bufferIndex];
let startOffset = this.offsetInBuffer(piece.bufferIndex, piece.start);
let endOffset = this.offsetInBuffer(piece.bufferIndex, piece.end);
let currentContent = buffer.buffer.substring(startOffset, endOffset);
return currentContent;
}
/**
* node node
* / \ / \

View file

@ -9,7 +9,7 @@
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { RunOnceScheduler, Delayer } from 'vs/base/common/async';
import { RunOnceScheduler, Delayer, asWinJsPromise } from 'vs/base/common/async';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
@ -30,11 +30,12 @@ import { IndentRangeProvider } from 'vs/editor/contrib/folding/indentRangeProvid
import { IPosition } from 'vs/editor/common/core/position';
import { FoldingProviderRegistry, FoldingRangeType } from 'vs/editor/common/modes';
import { SyntaxRangeProvider } from './syntaxRangeProvider';
import { CancellationToken } from 'vs/base/common/cancellation';
export const ID = 'editor.contrib.folding';
export interface RangeProvider {
compute(editorModel: ITextModel): TPromise<FoldingRegions>;
compute(editorModel: ITextModel, cancelationToken: CancellationToken): Thenable<FoldingRegions>;
}
export class FoldingController implements IEditorContribution {
@ -56,6 +57,7 @@ export class FoldingController implements IEditorContribution {
private hiddenRangeModel: HiddenRangeModel;
private rangeProvider: RangeProvider;
private foldingRegionPromise: TPromise<FoldingRegions>;
private foldingModelPromise: TPromise<FoldingModel>;
private updateScheduler: Delayer<FoldingModel>;
@ -171,6 +173,10 @@ export class FoldingController implements IEditorContribution {
this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e)));
this.localToDispose.push({
dispose: () => {
if (this.foldingRegionPromise) {
this.foldingRegionPromise.cancel();
this.foldingRegionPromise = null;
}
this.updateScheduler.cancel();
this.updateScheduler = null;
this.foldingModel = null;
@ -178,6 +184,7 @@ export class FoldingController implements IEditorContribution {
this.hiddenRangeModel = null;
this.cursorChangedScheduler = null;
this.rangeProvider = null;
}
});
this.onModelContentChanged();
@ -201,19 +208,24 @@ export class FoldingController implements IEditorContribution {
private onModelContentChanged() {
if (this.updateScheduler) {
if (this.foldingRegionPromise) {
this.foldingRegionPromise.cancel();
this.foldingRegionPromise = null;
}
this.foldingModelPromise = this.updateScheduler.trigger(() => {
if (this.foldingModel) { // null if editor has been disposed, or folding turned off
// some cursors might have moved into hidden regions, make sure they are in expanded regions
let selections = this.editor.getSelections();
let selectionLineNumbers = selections ? selections.map(s => s.startLineNumber) : [];
return this.getRangeProvider().compute(this.foldingModel.textModel).then(foldingRanges => {
if (this.foldingModel) { // null if editor has been disposed, or folding turned off
this.foldingModel.update(foldingRanges, selectionLineNumbers);
}
return this.foldingModel;
});
if (!this.foldingModel) { // null if editor has been disposed, or folding turned off
return null;
}
return null;
let foldingRegionPromise = this.foldingRegionPromise = asWinJsPromise<FoldingRegions>(token => this.getRangeProvider().compute(this.foldingModel.textModel, token));
return foldingRegionPromise.then(foldingRanges => {
if (foldingRanges && foldingRegionPromise === this.foldingRegionPromise) { // new request or cancelled in the meantime?
// some cursors might have moved into hidden regions, make sure they are in expanded regions
let selections = this.editor.getSelections();
let selectionLineNumbers = selections ? selections.map(s => s.startLineNumber) : [];
this.foldingModel.update(foldingRanges, selectionLineNumbers);
}
return this.foldingModel;
});
});
}
}

View file

@ -12,11 +12,12 @@ import { TextModel } from 'vs/editor/common/model/textModel';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { CancellationToken } from 'vs/base/common/cancellation';
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
export class IndentRangeProvider implements RangeProvider {
compute(editorModel: ITextModel): TPromise<FoldingRegions> {
compute(editorModel: ITextModel, cancelationToken: CancellationToken): Thenable<FoldingRegions> {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
let offSide = foldingRules && foldingRules.offSide;
let markers = foldingRules && foldingRules.markers;

View file

@ -7,11 +7,12 @@
import { FoldingProvider, IFoldingRange } from 'vs/editor/common/modes';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { asWinJsPromise } from 'vs/base/common/async';
import { toThenable } from 'vs/base/common/async';
import { ITextModel } from 'vs/editor/common/model';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { MAX_LINE_NUMBER, FoldingRegions } from './foldingRanges';
import { CancellationToken } from 'vs/base/common/cancellation';
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
@ -24,32 +25,42 @@ export class SyntaxRangeProvider implements RangeProvider {
constructor(private providers: FoldingProvider[]) {
}
compute(model: ITextModel): TPromise<FoldingRegions> {
return collectSyntaxRanges(this.providers, model).then(ranges => {
let res = sanitizeRanges(ranges);
//console.log(res.toString());
return res;
compute(model: ITextModel, cancellationToken: CancellationToken): Thenable<FoldingRegions> {
return collectSyntaxRanges(this.providers, model, cancellationToken).then(ranges => {
if (ranges) {
let res = sanitizeRanges(ranges);
return res;
}
return null;
});
}
}
function collectSyntaxRanges(providers: FoldingProvider[], model: ITextModel): TPromise<IFoldingRangeData[]> {
const rangeData: IFoldingRangeData[] = [];
let promises = providers.map((provider, rank) => asWinJsPromise(token => provider.provideFoldingRanges(model, token)).then(list => {
if (list && Array.isArray(list.ranges)) {
let nLines = model.getLineCount();
for (let r of list.ranges) {
if (r.startLineNumber > 0 && r.endLineNumber > r.startLineNumber && r.endLineNumber <= nLines) {
rangeData.push({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, rank, type: r.type });
function collectSyntaxRanges(providers: FoldingProvider[], model: ITextModel, cancellationToken: CancellationToken): Thenable<IFoldingRangeData[] | null> {
let promises = providers.map(provider => toThenable(provider.provideFoldingRanges(model, cancellationToken)));
return TPromise.join(promises).then(lists => {
let rangeData: IFoldingRangeData[] = null;
if (cancellationToken.isCancellationRequested) {
return null;
}
for (let i = 0; i < lists.length; i++) {
let list = lists[i];
if (list && Array.isArray(list.ranges)) {
if (!Array.isArray(rangeData)) {
rangeData = [];
}
let nLines = model.getLineCount();
for (let r of list.ranges) {
if (r.startLineNumber > 0 && r.endLineNumber > r.startLineNumber && r.endLineNumber <= nLines) {
rangeData.push({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, rank: i, type: r.type });
}
}
}
}
}, onUnexpectedExternalError));
return TPromise.join(promises).then(() => {
return rangeData;
});
}, onUnexpectedExternalError);
}
export class RangesCollector {

View file

@ -17,12 +17,16 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MarkerNavigationWidget } from './gotoErrorWidget';
import { compare } from 'vs/base/common/strings';
import { binarySearch } from 'vs/base/common/arrays';
import { IEditorService } from 'vs/platform/editor/common/editor';
import { TPromise } from 'vs/base/common/winjs.base';
class MarkerModel {
@ -47,9 +51,13 @@ class MarkerModel {
// listen on editor
this._toUnbind.push(this._editor.onDidDispose(() => this.dispose()));
this._toUnbind.push(this._editor.onDidChangeCursorPosition(() => {
if (!this._ignoreSelectionChange) {
this._nextIdx = -1;
if (this._ignoreSelectionChange) {
return;
}
if (this.currentMarker && Range.containsPosition(this.currentMarker, this._editor.getPosition())) {
return;
}
this._nextIdx = -1;
}));
}
@ -62,13 +70,15 @@ class MarkerModel {
}
public setMarkers(markers: IMarker[]): void {
// assign
let oldMarker = this._nextIdx >= 0 ? this._markers[this._nextIdx] : undefined;
this._markers = markers || [];
// sort markers
this._markers.sort((left, right) => Severity.compare(left.severity, right.severity) || Range.compareRangesUsingStarts(left, right));
this._nextIdx = -1;
this._markers.sort(MarkerNavigationAction.compareMarker);
if (!oldMarker) {
this._nextIdx = -1;
} else {
this._nextIdx = Math.max(-1, binarySearch(this._markers, oldMarker, MarkerNavigationAction.compareMarker));
}
this._onMarkerSetChanged.fire(this);
}
@ -95,7 +105,7 @@ class MarkerModel {
}
if (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition())) {
this._nextIdx = i + (fwd ? 0 : -1);
this._nextIdx = i;
found = true;
break;
}
@ -109,42 +119,49 @@ class MarkerModel {
}
}
private move(fwd: boolean): void {
get currentMarker(): IMarker {
return this.canNavigate() ? this._markers[this._nextIdx] : undefined;
}
public move(fwd: boolean, inCircles: boolean): boolean {
if (!this.canNavigate()) {
this._onCurrentMarkerChanged.fire(undefined);
return;
return !inCircles;
}
let oldIdx = this._nextIdx;
let atEdge = false;
if (this._nextIdx === -1) {
this._initIdx(fwd);
} else if (fwd) {
this._nextIdx += 1;
if (this._nextIdx >= this._markers.length) {
this._nextIdx = 0;
if (inCircles || this._nextIdx + 1 < this._markers.length) {
this._nextIdx = (this._nextIdx + 1) % this._markers.length;
} else {
atEdge = true;
}
} else {
this._nextIdx -= 1;
if (this._nextIdx < 0) {
this._nextIdx = this._markers.length - 1;
} else if (!fwd) {
if (inCircles || this._nextIdx > 0) {
this._nextIdx = (this._nextIdx - 1 + this._markers.length) % this._markers.length;
} else {
atEdge = true;
}
}
const marker = this._markers[this._nextIdx];
this._onCurrentMarkerChanged.fire(marker);
if (oldIdx !== this._nextIdx) {
const marker = this._markers[this._nextIdx];
this._onCurrentMarkerChanged.fire(marker);
}
return atEdge;
}
public canNavigate(): boolean {
return this._markers.length > 0;
}
public next(): void {
this.move(true);
}
public previous(): void {
this.move(false);
}
public findMarkerAtPosition(pos: Position): IMarker {
for (const marker of this._markers) {
if (Range.containsPosition(marker, pos)) {
@ -167,30 +184,6 @@ class MarkerModel {
}
}
class MarkerNavigationAction extends EditorAction {
private _isNext: boolean;
constructor(next: boolean, opts: IActionOptions) {
super(opts);
this._isNext = next;
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const controller = MarkerController.get(editor);
if (!controller) {
return;
}
const model = controller.getOrCreateModel();
if (this._isNext) {
model.next();
} else {
model.previous();
}
}
}
class MarkerController implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.markerController';
@ -285,6 +278,79 @@ class MarkerController implements editorCommon.IEditorContribution {
}
}
class MarkerNavigationAction extends EditorAction {
private _isNext: boolean;
constructor(next: boolean, opts: IActionOptions) {
super(opts);
this._isNext = next;
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const markerService = accessor.get(IMarkerService);
const editorService = accessor.get(IEditorService);
const controller = MarkerController.get(editor);
if (!controller) {
return undefined;
}
const model = controller.getOrCreateModel();
const atEdge = model.move(this._isNext, false);
if (!atEdge) {
return undefined;
}
// try with the next/prev file
let oldMarker = model.currentMarker || { resource: editor.getModel().uri, severity: Severity.Error, startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 };
let markers = markerService.read().sort(MarkerNavigationAction.compareMarker);
let idx = binarySearch(markers, oldMarker, MarkerNavigationAction.compareMarker);
if (idx < 0) {
// find best match...
idx = ~idx;
idx %= markers.length;
} else if (this._isNext) {
idx = (idx + 1) % markers.length;
} else {
idx = (idx + markers.length - 1) % markers.length;
}
let newMarker = markers[idx];
if (newMarker.resource.toString() === editor.getModel().uri.toString()) {
// the next `resource` is this resource which
// means we cycle within this file
model.move(this._isNext, true);
return undefined;
}
// close the widget for this editor-instance, open the resource
// for the next marker and re-start marker navigation in there
controller.closeMarkersNavigation();
return editorService.openEditor({
resource: newMarker.resource,
options: { pinned: false, revealIfOpened: true, revealInCenterIfOutsideViewport: true, selection: newMarker }
}).then(editor => {
if (!editor || !isCodeEditor(editor.getControl())) {
return undefined;
}
return (<ICodeEditor>editor.getControl()).getAction(this.id).run();
});
}
static compareMarker(a: IMarker, b: IMarker): number {
let res = compare(a.resource.toString(), b.resource.toString());
if (res === 0) {
res = Severity.compare(a.severity, b.severity);
}
if (res === 0) {
res = Range.compareRangesUsingStarts(a, b);
}
return res;
}
}
class NextMarkerAction extends MarkerNavigationAction {
constructor() {
super(true, {

View file

@ -184,9 +184,9 @@ class RenameController implements IEditorContribution {
}, err => {
this._renameInputVisible.reset();
this.editor.focus();
if (!isPromiseCanceledError(err)) {
this.editor.focus();
return TPromise.wrapError(err);
}
return undefined;

View file

@ -20,9 +20,9 @@ export interface ICompletionItem extends ISuggestionItem {
/* __GDPR__FRAGMENT__
"ICompletionStats" : {
"suggestionCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"snippetCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"textCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"suggestionCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"snippetCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"textCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
// __GDPR__TODO__: This is a dynamically extensible structure which can not be declared statically.

View file

@ -708,7 +708,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
stats['wasAutomaticallyTriggered'] = !!isAuto;
/* __GDPR__
"suggestWidget" : {
"wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"${include}": [
"${ICompletionStats}",
"${EditorTelemetryData}"

View file

@ -19,7 +19,7 @@ import { IdGenerator } from 'vs/base/common/idGenerator';
import { createCSSRule } from 'vs/base/browser/dom';
import URI from 'vs/base/common/uri';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { isWindows } from 'vs/base/common/platform';
import { isWindows, isLinux } from 'vs/base/common/platform';
// The alternative key on all platforms is alt. On windows we also support shift as an alternative key #44136
class AlternativeKeyEmitter extends Emitter<boolean> {
@ -31,7 +31,7 @@ class AlternativeKeyEmitter extends Emitter<boolean> {
super();
this._subscriptions.push(domEvent(document.body, 'keydown')(e => {
this.isPressed = e.altKey || (isWindows && e.shiftKey);
this.isPressed = e.altKey || ((isWindows || isLinux) && e.shiftKey);
}));
this._subscriptions.push(domEvent(document.body, 'keyup')(e => this.isPressed = false));
this._subscriptions.push(domEvent(document.body, 'mouseleave')(e => this.isPressed = false));

View file

@ -85,7 +85,7 @@ export class ConfigurationModel implements IConfigurationModel {
if (override) {
this.mergeContents(override.contents, otherOverride.contents);
} else {
overrides.push(otherOverride);
overrides.push(objects.deepClone(otherOverride));
}
}
for (const key of other.keys) {

View file

@ -199,6 +199,17 @@ suite('ConfigurationModel', () => {
assert.deepEqual(result.keys, ['a.b', 'f']);
});
test('merge overrides when frozen', () => {
let model1 = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f'], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]).freeze();
let model2 = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } } }]).freeze();
let result = new ConfigurationModel().merge(model1, model2);
assert.deepEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } } }]);
assert.deepEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
assert.deepEqual(result.keys, ['a.b', 'f']);
});
test('Test contents while getting an existing property', () => {
let testObject = new ConfigurationModel({ 'a': 1 });
assert.deepEqual(testObject.getValue('a'), 1);
@ -349,24 +360,6 @@ suite('CustomConfigurationModel', () => {
});
assert.equal(true, new DefaultConfigurationModel().getValue('a'));
});
test('Test registering the language property', () => {
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
'id': '[a]',
'order': 1,
'title': 'a',
'type': 'object',
'properties': {
'[a]': {
'description': 'a',
'type': 'boolean',
'default': false,
}
}
});
assert.equal(undefined, new DefaultConfigurationModel().getValue('[a]'));
});
});
suite('ConfigurationChangeEvent', () => {

View file

@ -146,6 +146,12 @@ export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifie
&& (!thing.uuid || typeof thing.uuid === 'string');
}
/* __GDPR__FRAGMENT__
"ExtensionIdentifier" : {
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"uuid": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
export interface IExtensionIdentifier {
id: string;
uuid?: string;

View file

@ -85,7 +85,7 @@ export function getLocalExtensionTelemetryData(extension: ILocalExtension): any
"publisherId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"publisherName": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"publisherDisplayName": { "classification": "PublicPersonalData", "purpose": "FeatureInsight" },
"dependencies": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"dependencies": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"${include}": [
"${GalleryExtensionTelemetryData2}"
]

View file

@ -303,7 +303,7 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
},
/* __GDPR__FRAGMENT__
"GalleryExtensionTelemetryData2" : {
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"searchText": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"querySource": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
@ -484,7 +484,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
const startTime = new Date().getTime();
/* __GDPR__
"galleryService:downloadVSIX" : {
"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"${include}": [
"${GalleryExtensionTelemetryData}"
]
@ -634,7 +634,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
/* __GDPR__
"galleryService:requestError" : {
"url" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"cdn": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"cdn": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
}
*/

View file

@ -5,25 +5,26 @@
'use strict';
import { ITree, ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
import { List, IListOptions, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, IMultipleSelectionController, IOpenController } from 'vs/base/browser/ui/list/listWidget';
import { List, IListOptions, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, IMultipleSelectionController, IOpenController, DefaultStyleController } from 'vs/base/browser/ui/list/listWidget';
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, toDisposable, combinedDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { PagedList, IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { IDelegate, IRenderer, IListMouseEvent, IListTouchEvent } from 'vs/base/browser/ui/list/list';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { attachListStyler, defaultListStyles, computeStyles } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { mixin } from 'vs/base/common/objects';
import { localize } from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { DefaultController, IControllerOptions, OpenMode, ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults';
import { DefaultController, IControllerOptions, OpenMode, ClickBehavior, DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import Event, { Emitter } from 'vs/base/common/event';
import { createStyleSheet } from 'vs/base/browser/dom';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
export type ListWidget = List<any> | PagedList<any> | ITree;
@ -105,6 +106,7 @@ function createScopedContextKeyService(contextKeyService: IContextKeyService, wi
export const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier';
export const openModeSettingKey = 'workbench.list.openMode';
export const horizontalScrollingKey = 'workbench.tree.horizontalScrolling';
function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean {
return configurationService.getValue(multiSelectModifierSettingKey) === 'alt';
@ -163,11 +165,33 @@ function handleListControllers<T>(options: IListOptions<T>, configurationService
return options;
}
let sharedListStyleSheet: HTMLStyleElement;
function getSharedListStyleSheet(): HTMLStyleElement {
if (!sharedListStyleSheet) {
sharedListStyleSheet = createStyleSheet();
}
return sharedListStyleSheet;
}
let sharedTreeStyleSheet: HTMLStyleElement;
function getSharedTreeStyleSheet(): HTMLStyleElement {
if (!sharedTreeStyleSheet) {
sharedTreeStyleSheet = createStyleSheet();
}
return sharedTreeStyleSheet;
}
function handleTreeController(configuration: ITreeConfiguration, instantiationService: IInstantiationService): ITreeConfiguration {
if (!configuration.controller) {
configuration.controller = instantiationService.createInstance(WorkbenchTreeController, {});
}
if (!configuration.styler) {
configuration.styler = new DefaultTreestyler(getSharedTreeStyleSheet());
}
return configuration;
}
@ -190,7 +214,15 @@ export class WorkbenchList<T> extends List<T> {
@IThemeService themeService: IThemeService,
@IConfigurationService private configurationService: IConfigurationService
) {
super(container, delegate, renderers, mixin(handleListControllers(options, configurationService), { keyboardSupport: false, selectOnMouseDown: true } as IListOptions<T>, false));
super(container, delegate, renderers,
{
keyboardSupport: false,
selectOnMouseDown: true,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...handleListControllers(options, configurationService)
} as IListOptions<T>
);
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
@ -243,7 +275,15 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
@IThemeService themeService: IThemeService,
@IConfigurationService private configurationService: IConfigurationService
) {
super(container, delegate, renderers, mixin(handleListControllers(options, configurationService), { keyboardSupport: false, selectOnMouseDown: true } as IListOptions<T>, false));
super(container, delegate, renderers,
{
keyboardSupport: false,
selectOnMouseDown: true,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...handleListControllers(options, configurationService)
} as IListOptions<T>
);
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
@ -281,7 +321,7 @@ export class WorkbenchTree extends Tree {
readonly contextKeyService: IContextKeyService;
protected disposables: IDisposable[] = [];
protected disposables: IDisposable[];
private listDoubleSelection: IContextKey<boolean>;
private listMultiSelection: IContextKey<boolean>;
@ -297,10 +337,20 @@ export class WorkbenchTree extends Tree {
@IListService listService: IListService,
@IThemeService themeService: IThemeService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService private configurationService: IConfigurationService
@IConfigurationService configurationService: IConfigurationService
) {
super(container, handleTreeController(configuration, instantiationService), mixin(options, { keyboardSupport: false } as ITreeOptions, false));
const config = handleTreeController(configuration, instantiationService);
const horizontalScrollMode = configurationService.getValue(horizontalScrollingKey) ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden;
const opts = {
horizontalScrollMode,
keyboardSupport: false,
...computeStyles(themeService.getTheme(), defaultListStyles),
...options
};
super(container, config, opts);
this.disposables = [];
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
this.listMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService);
@ -314,23 +364,19 @@ export class WorkbenchTree extends Tree {
attachListStyler(this, themeService)
);
this.registerListeners();
}
private registerListeners(): void {
this.disposables.push(this.onDidChangeSelection(() => {
const selection = this.getSelection();
this.listDoubleSelection.set(selection && selection.length === 2);
this.listMultiSelection.set(selection && selection.length > 1);
}));
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => {
this.disposables.push(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(openModeSettingKey)) {
this._openOnSingleClick = useSingleClickToOpen(this.configurationService);
this._openOnSingleClick = useSingleClickToOpen(configurationService);
}
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService);
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
}
}));
}
@ -487,7 +533,7 @@ configurationRegistry.registerConfiguration({
'title': localize('workbenchConfigurationTitle', "Workbench"),
'type': 'object',
'properties': {
'workbench.list.multiSelectModifier': {
[multiSelectModifierSettingKey]: {
'type': 'string',
'enum': ['ctrlCmd', 'alt'],
'enumDescriptions': [
@ -503,7 +549,7 @@ configurationRegistry.registerConfiguration({
]
}, "The modifier to be used to add an item in trees and lists to a multi-selection with the mouse (for example in the explorer, open editors and scm view). `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The 'Open to Side' mouse gestures - if supported - will adapt such that they do not conflict with the multiselect modifier.")
},
'workbench.list.openMode': {
[openModeSettingKey]: {
'type': 'string',
'enum': ['singleClick', 'doubleClick'],
'enumDescriptions': [
@ -515,6 +561,11 @@ configurationRegistry.registerConfiguration({
key: 'openModeModifier',
comment: ['`singleClick` and `doubleClick` refers to a value the setting can take and should not be localized.']
}, "Controls how to open items in trees and lists using the mouse (if supported). Set to `singleClick` to open items with a single mouse click and `doubleClick` to only open via mouse double click. For parents with children in trees, this setting will control if a single click expands the parent or a double click. Note that some trees and lists might choose to ignore this setting if it is not applicable. ")
},
[horizontalScrollingKey]: {
'type': 'boolean',
'default': false,
'description': localize('horizontalScrolling setting', "Controls whether trees support horizontal scrolling in the workbench.")
}
}
});

View file

@ -82,11 +82,12 @@ export enum QueryType {
/* __GDPR__FRAGMENT__
"IPatternInfo" : {
"pattern" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"isRegExp": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isWordMatch": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isRegExp": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"isWordMatch": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"wordSeparators": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isMultiline": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isCaseSensitive": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"isMultiline": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"isCaseSensitive": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"isSmartCase": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
export interface IPatternInfo {

View file

@ -149,8 +149,8 @@ export default class ErrorTelemetry {
"name": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"stack": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"id": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"line": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"column": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }
"line": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"column": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
// __GDPR__TODO__ what's the complete set of properties?

View file

@ -11,7 +11,6 @@ import { deepClone } from 'vs/base/common/objects';
/* __GDPR__FRAGMENT__
"IExperiments" : {
"deployToAzureQuickLink" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
export interface IExperiments {

View file

@ -67,7 +67,7 @@ export class TelemetryService implements ITelemetryService {
this._configurationService.onDidChangeConfiguration(this._updateUserOptIn, this, this._disposables);
/* __GDPR__
"optInStatus" : {
"optIn" : { "classification": "SystemMetaData", "purpose": "BusinessInsight" }
"optIn" : { "classification": "SystemMetaData", "purpose": "BusinessInsight", "isMeasurement": true }
}
*/
this.publicLog('optInStatus', { optIn: this._userOptIn });

View file

@ -42,7 +42,7 @@ export const NullAppender: ITelemetryAppender = { log: () => null };
"URIDescriptor" : {
"mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"path": { "classification": "CustomerContent", "purpose": "FeatureInsight" }
"path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
export interface URIDescriptor {

View file

@ -39,12 +39,12 @@ export function resolveCommonProperties(commit: string, version: string, machine
get: () => new Date(),
enumerable: true
},
// __GDPR__COMMON__ "common.timesincesessionstart" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
// __GDPR__COMMON__ "common.timesincesessionstart" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
'common.timesincesessionstart': {
get: () => Date.now() - startTime,
enumerable: true
},
// __GDPR__COMMON__ "common.sequence" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
// __GDPR__COMMON__ "common.sequence" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
'common.sequence': {
get: () => seq++,
enumerable: true

View file

@ -26,7 +26,7 @@ export function resolveWorkbenchCommonProperties(storageService: IStorageService
result['common.lastSessionDate'] = lastSessionDate;
// __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
result['common.isNewSession'] = !lastSessionDate ? '1' : '0';
// __GDPR__COMMON__ "common.instanceId" : { "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
// __GDPR__COMMON__ "common.instanceId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
result['common.instanceId'] = getOrCreateInstanceId(storageService);
return result;

View file

@ -8,8 +8,10 @@
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusForeground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, lighten, badgeBackground, badgeForeground, progressBarBackground } from 'vs/platform/theme/common/colorRegistry';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
export type styleFn = (colors: { [name: string]: ColorIdentifier }) => void;
export type styleFn = (colors: { [name: string]: Color }) => void;
export interface IStyleOverrides {
[color: string]: ColorIdentifier;
@ -23,17 +25,27 @@ export interface IColorMapping {
[optionsKey: string]: ColorIdentifier | ColorFunction | undefined;
}
export function attachStyler<T extends IColorMapping>(themeService: IThemeService, optionsMapping: T, widgetOrCallback: IThemable | styleFn): IDisposable {
function applyStyles(theme: ITheme): void {
const styles = Object.create(null);
for (let key in optionsMapping) {
const value = optionsMapping[key as string];
if (typeof value === 'string') {
styles[key] = theme.getColor(value);
} else if (typeof value === 'function') {
styles[key] = value(theme);
}
export interface IComputedStyles {
[color: string]: Color;
}
export function computeStyles(theme: ITheme, styleMap: IColorMapping): IComputedStyles {
const styles = Object.create(null) as IComputedStyles;
for (let key in styleMap) {
const value = styleMap[key as string];
if (typeof value === 'string') {
styles[key] = theme.getColor(value);
} else if (typeof value === 'function') {
styles[key] = value(theme);
}
}
return styles;
}
export function attachStyler<T extends IColorMapping>(themeService: IThemeService, styleMap: T, widgetOrCallback: IThemable | styleFn): IDisposable {
function applyStyles(theme: ITheme): void {
const styles = computeStyles(themeService.getTheme(), styleMap);
if (typeof widgetOrCallback === 'function') {
widgetOrCallback(styles);
@ -202,28 +214,29 @@ export interface IListStyleOverrides extends IStyleOverrides {
listHoverOutline?: ColorIdentifier;
}
export function attachListStyler(widget: IThemable, themeService: IThemeService, style?: IListStyleOverrides): IDisposable {
return attachStyler(themeService, {
listFocusBackground: (style && style.listFocusBackground) || listFocusBackground,
listFocusForeground: (style && style.listFocusForeground) || listFocusForeground,
listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || lighten(listActiveSelectionBackground, 0.1),
listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || listActiveSelectionForeground,
listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || listActiveSelectionBackground,
listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || listActiveSelectionForeground,
listInactiveSelectionBackground: (style && style.listInactiveSelectionBackground) || listInactiveSelectionBackground,
listInactiveSelectionForeground: (style && style.listInactiveSelectionForeground) || listInactiveSelectionForeground,
listInactiveFocusBackground: (style && style.listInactiveFocusBackground) || listInactiveFocusBackground,
listInactiveFocusForeground: (style && style.listInactiveFocusForeground) || listInactiveFocusForeground,
listHoverBackground: (style && style.listHoverBackground) || listHoverBackground,
listHoverForeground: (style && style.listHoverForeground) || listHoverForeground,
listDropBackground: (style && style.listDropBackground) || listDropBackground,
listFocusOutline: (style && style.listFocusOutline) || activeContrastBorder,
listSelectionOutline: (style && style.listSelectionOutline) || activeContrastBorder,
listHoverOutline: (style && style.listHoverOutline) || activeContrastBorder,
listInactiveFocusOutline: style && style.listInactiveFocusOutline // not defined by default, only opt-in
} as IListStyleOverrides, widget);
export function attachListStyler(widget: IThemable, themeService: IThemeService, overrides?: IListStyleOverrides): IDisposable {
return attachStyler(themeService, mixin(overrides || Object.create(null), defaultListStyles, false) as IListStyleOverrides, widget);
}
export const defaultListStyles: IColorMapping = {
listFocusBackground: listFocusBackground,
listFocusForeground: listFocusForeground,
listActiveSelectionBackground: lighten(listActiveSelectionBackground, 0.1),
listActiveSelectionForeground: listActiveSelectionForeground,
listFocusAndSelectionBackground: listActiveSelectionBackground,
listFocusAndSelectionForeground: listActiveSelectionForeground,
listInactiveSelectionBackground: listInactiveSelectionBackground,
listInactiveSelectionForeground: listInactiveSelectionForeground,
listInactiveFocusBackground: listInactiveFocusBackground,
listInactiveFocusForeground: listInactiveFocusForeground,
listHoverBackground: listHoverBackground,
listHoverForeground: listHoverForeground,
listDropBackground: listDropBackground,
listFocusOutline: activeContrastBorder,
listSelectionOutline: activeContrastBorder,
listHoverOutline: activeContrastBorder
};
export interface IButtonStyleOverrides extends IStyleOverrides {
buttonForeground?: ColorIdentifier;
buttonBackground?: ColorIdentifier;

View file

@ -94,7 +94,7 @@ export class DarwinUpdateService extends AbstractUpdateService {
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!this.state.context });

View file

@ -53,7 +53,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
if (!update || !update.url || !update.version || !update.productVersion) {
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
@ -68,7 +68,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });

View file

@ -89,7 +89,7 @@ export class Win32UpdateService extends AbstractUpdateService {
if (!update || !update.url || !update.version) {
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
@ -134,7 +134,7 @@ export class Win32UpdateService extends AbstractUpdateService {
this.logService.error(err);
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });

115
src/vs/vscode.d.ts vendored
View file

@ -4332,6 +4332,38 @@ declare module 'vscode' {
options?: ProcessExecutionOptions;
}
/**
* The shell quoting options.
*/
export interface ShellQuotingOptions {
/**
* The character used to do character escaping. If a string is provided only spaces
* are escaped. If a `{ escapeChar, charsToEscape }` literal is provide all characters
* in `charsToEscape` are escaped using the `escapeChar`.
*/
escape?: string | {
/**
* The escape character.
*/
escapeChar: string;
/**
* The characters to escape.
*/
charsToEscape: string;
};
/**
* The character used for strong quoting. The string's length must be 1.
*/
strong?: string;
/**
* The character used for weak quoting. The string's length must be 1.
*/
weak?: string;
}
/**
* Options for a shell execution
*/
@ -4346,6 +4378,11 @@ declare module 'vscode' {
*/
shellArgs?: string[];
/**
* The shell quotes supported by this shell.
*/
shellQuoting?: ShellQuotingOptions;
/**
* The current working directory of the executed shell.
* If omitted the tools current workspace root is used.
@ -4360,10 +4397,55 @@ declare module 'vscode' {
env?: { [key: string]: string };
}
/**
* Defines how an argument should be quoted if it contains
* spaces or unsuppoerted characters.
*/
export enum ShellQuoting {
/**
* Character escaping should be used. This for example
* uses \ on bash and ` on PowerShell.
*/
Escape = 1,
/**
* Strong string quoting should be used. This for example
* uses " for Windows cmd and ' for bash and PowerShell.
* Strong quoting treats arguments as literal strings.
* Under PowerShell echo 'The value is $(2 * 3)' will
* print `The value is $(2 * 3)`
*/
Strong = 2,
/**
* Weak string quoting should be used. This for example
* uses " for Windows cmd, bash and PowerShell. Weak quoting
* still performs some kind of evaluation inside the quoted
* string. Under PowerShell echo "The value is $(2 * 3)"
* will print `The value is 6`
*/
Weak = 3
}
/**
* A string that will be quoted depending on the used shell.
*/
export interface ShellQuotedString {
/**
* The actual string value.
*/
value: string;
/**
* The quoting style to use.
*/
quoting: ShellQuoting;
}
export class ShellExecution {
/**
* Creates a process execution.
* Creates a shell execution with a full command line.
*
* @param commandLine The command line to execute.
* @param options Optional options for the started the shell.
@ -4371,10 +4453,32 @@ declare module 'vscode' {
constructor(commandLine: string, options?: ShellExecutionOptions);
/**
* The shell command line
* Creates a shell execution with a command and arguments. For the real execution VS Code will
* construct a command line from the command and the arguments. This is subject to interpretation
* especially when it comes to quoting. If full control over the command line is needed please
* use the constructor that creates a `ShellExecution` with the full command line.
*
* @param command The command to execute.
* @param args The command arguments.
* @param options Optional options for the started the shell.
*/
constructor(command: string | ShellQuotedString, args: (string | ShellQuotedString)[], options?: ShellExecutionOptions);
/**
* The shell command line. Is `undefined` if created with a command and arguments.
*/
commandLine: string;
/**
* The shell command. Is `undefined` if created with a full command line.
*/
command: string | ShellQuotedString;
/**
* The shell args. Is `undefined` if created with a full command line.
*/
args: (string | ShellQuotedString)[];
/**
* The shell options used when the command line is executed in a shell.
* Defaults to undefined.
@ -4496,9 +4600,12 @@ declare module 'vscode' {
/**
* Resolves a task that has no [`execution`](#Task.execution) set. Tasks are
* often created from information found in the `task.json`-file. Such tasks miss
* often created from information found in the `tasks.json`-file. Such tasks miss
* the information on how to execute them and a task provider must fill in
* the missing information in the `resolveTask`-method.
* the missing information in the `resolveTask`-method. This method will not be
* called for tasks returned from the above `provideTasks` method since those
* tasks are always fully resolved. A valid default implementation for the
* `resolveTask` method is to return `undefined`.
*
* @param task The task to resolve.
* @param token A cancellation token.

View file

@ -373,6 +373,9 @@ declare module 'vscode' {
}
}
export interface FoldingProvider {
/**
* Returns a list of folding ranges or null if the provider does not want to participate or was cancelled.
*/
provideFoldingRanges(document: TextDocument, token: CancellationToken): ProviderResult<FoldingRangeList>;
}

View file

@ -25,7 +25,7 @@ export class MainThreadTelemetry implements MainThreadTelemetryShape {
}
$publicLog(eventName: string, data: any = Object.create(null)): void {
// __GDPR__COMMON__ "pluginHostTelemetry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
// __GDPR__COMMON__ "pluginHostTelemetry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
data[MainThreadTelemetry._name] = true;
this._telemetryService.publicLog(eventName, data);
}

View file

@ -638,6 +638,7 @@ export function createApiFactory(
TaskGroup: extHostTypes.TaskGroup,
ProcessExecution: extHostTypes.ProcessExecution,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
TaskScope: extHostTypes.TaskScope,
Task: extHostTypes.Task,
ConfigurationTarget: extHostTypes.ConfigurationTarget,

View file

@ -278,20 +278,43 @@ namespace CommandOptions {
}
}
namespace ShellQuoteOptions {
export function from(value: vscode.ShellQuotingOptions): TaskSystem.ShellQuotingOptions {
if (value === void 0 || value === null) {
return undefined;
}
return {
escape: value.escape,
strong: value.strong,
weak: value.strong
};
}
}
namespace ShellConfiguration {
export function from(value: { executable?: string, shellArgs?: string[] }): TaskSystem.ShellConfiguration {
export function from(value: { executable?: string, shellArgs?: string[], quotes?: vscode.ShellQuotingOptions }): TaskSystem.ShellConfiguration {
if (value === void 0 || value === null || !value.executable) {
return undefined;
}
let result: TaskSystem.ShellConfiguration = {
executable: value.executable,
args: Strings.from(value.shellArgs)
args: Strings.from(value.shellArgs),
quoting: ShellQuoteOptions.from(value.quotes)
};
return result;
}
}
namespace ShellString {
export function from(value: (string | vscode.ShellQuotedString)[]): TaskSystem.CommandString[] {
if (value === void 0 || value === null) {
return undefined;
}
return value.slice(0);
}
}
namespace Tasks {
export function from(tasks: vscode.Task[], rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): TaskSystem.ContributedTask[] {
@ -396,18 +419,34 @@ namespace Tasks {
}
function getShellCommand(value: vscode.ShellExecution): TaskSystem.CommandConfiguration {
if (typeof value.commandLine !== 'string') {
return undefined;
if (value.args) {
if (typeof value.command !== 'string' && typeof value.command.value !== 'string') {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
name: value.command,
args: ShellString.from(value.args),
runtime: TaskSystem.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
} else {
if (typeof value.commandLine !== 'string') {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
name: value.commandLine,
runtime: TaskSystem.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
}
let result: TaskSystem.CommandConfiguration = {
name: value.commandLine,
runtime: TaskSystem.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
}
}

View file

@ -1311,14 +1311,30 @@ export class ProcessExecution implements vscode.ProcessExecution {
export class ShellExecution implements vscode.ShellExecution {
private _commandLine: string;
private _command: string | vscode.ShellQuotedString;
private _args: (string | vscode.ShellQuotedString)[];
private _options: vscode.ShellExecutionOptions;
constructor(commandLine: string, options?: vscode.ShellExecutionOptions) {
if (typeof commandLine !== 'string') {
throw illegalArgument('commandLine');
constructor(commandLine: string, options?: vscode.ShellExecutionOptions);
constructor(command: string | vscode.ShellQuotedString, args: (string | vscode.ShellQuotedString)[], options?: vscode.ShellExecutionOptions);
constructor(arg0: string | vscode.ShellQuotedString, arg1?: vscode.ShellExecutionOptions | (string | vscode.ShellQuotedString)[], arg2?: vscode.ShellExecutionOptions) {
if (Array.isArray(arg1)) {
if (!arg0) {
throw illegalArgument('command can\'t be undefined or null');
}
if (typeof arg0 !== 'string' && typeof arg0.value !== 'string') {
throw illegalArgument('command');
}
this._command = arg0;
this._args = arg1 as (string | vscode.ShellQuotedString)[];
this._options = arg2;
} else {
if (typeof arg0 !== 'string') {
throw illegalArgument('commandLine');
}
this._commandLine = arg0;
this._options = arg1;
}
this._commandLine = commandLine;
this._options = options;
}
get commandLine(): string {
@ -1332,6 +1348,25 @@ export class ShellExecution implements vscode.ShellExecution {
this._commandLine = value;
}
get command(): string | vscode.ShellQuotedString {
return this._command;
}
set command(value: string | vscode.ShellQuotedString) {
if (typeof value !== 'string' && typeof value.value !== 'string') {
throw illegalArgument('command');
}
this._command = value;
}
get args(): (string | vscode.ShellQuotedString)[] {
return this._args;
}
set args(value: (string | vscode.ShellQuotedString)[]) {
this._args = value || [];
}
get options(): vscode.ShellExecutionOptions {
return this._options;
}
@ -1341,6 +1376,12 @@ export class ShellExecution implements vscode.ShellExecution {
}
}
export enum ShellQuoting {
Escape = 1,
Strong = 2,
Weak = 3
}
export enum TaskScope {
Global = 1,
Workspace = 2

View file

@ -26,11 +26,11 @@ class ToggleCenteredLayout extends Action {
}
public run(): TPromise<any> {
this.partService.toggleCenteredEditorLayout();
this.partService.centerEditorLayout(!this.partService.isEditorLayoutCentered());
return TPromise.as(null);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View"));
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View"));

View file

@ -25,6 +25,7 @@ import { Schemas } from 'vs/base/common/network';
import { FileKind, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { ITextModel } from 'vs/editor/common/model';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import Event, { Emitter } from 'vs/base/common/event';
export interface IResourceLabel {
name: string;
@ -38,12 +39,16 @@ export interface IResourceLabelOptions extends IIconLabelValueOptions {
}
export class ResourceLabel extends IconLabel {
private toDispose: IDisposable[];
private label: IResourceLabel;
private options: IResourceLabelOptions;
private computedIconClasses: string[];
private lastKnownConfiguredLangId: string;
private _onDidRender = new Emitter<void>();
readonly onDidRender: Event<void> = this._onDidRender.event;
constructor(
container: HTMLElement,
options: IIconLabelCreationOptions,
@ -217,6 +222,7 @@ export class ResourceLabel extends IconLabel {
}
this.setValue(label, this.label.description, iconLabelOptions);
this._onDidRender.fire();
}
public dispose(): void {

View file

@ -207,6 +207,10 @@ export class ActivityActionItem extends BaseActionItem {
}
protected updateBadge(badge: IBadge, clazz?: string): void {
if (!this.$badge || !this.$badgeContent) {
return;
}
this.badgeDisposable.dispose();
this.badgeDisposable = empty;

View file

@ -1593,8 +1593,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
// TODO@Ben remove me after a while
/* __GDPR__
"editorGroupMoved" : {
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('editorGroupMoved', { source: position, to: moveTo });
@ -2139,7 +2139,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
// Layout centered Editor (only in vertical layout when one group is opened)
const id = this.visibleEditors[Position.ONE] ? this.visibleEditors[Position.ONE].getId() : undefined;
const doCentering = this.partService.isEditorLayoutCentered() && this.stacks.groups.length === 1 && id !== PREFERENCES_EDITOR_ID && id !== TEXT_DIFF_EDITOR_ID;
const doCentering = this.layoutVertically && this.partService.isEditorLayoutCentered() && this.stacks.groups.length === 1 && id !== PREFERENCES_EDITOR_ID && id !== TEXT_DIFF_EDITOR_ID;
if (doCentering && !this.centeredEditorActive) {
this.centeredEditorSashLeft.show();
this.centeredEditorSashRight.show();

View file

@ -20,7 +20,7 @@ import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/brow
import { KeyCode } from 'vs/base/common/keyCodes';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@ -34,7 +34,6 @@ import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecyc
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { getOrSet } from 'vs/base/common/map';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_BACKGROUND, WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
import { activeContrastBorder, contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
@ -84,34 +83,6 @@ export class TabsTitleControl extends TitleControl {
this.editorLabels = [];
}
protected initActions(services: IInstantiationService): void {
super.initActions(this.createScopedInstantiationService());
}
private createScopedInstantiationService(): IInstantiationService {
const stacks = this.editorGroupService.getStacksModel();
const delegatingEditorService = this.instantiationService.createInstance(DelegatingWorkbenchEditorService);
// We create a scoped instantiation service to override the behaviour when closing an inactive editor
// Specifically we want to move focus back to the editor when an inactive editor is closed from anywhere
// in the tabs title control (e.g. mouse middle click, context menu on tab). This is only needed for
// the inactive editors because closing the active one will always cause a tab switch that sets focus.
// We also want to block the tabs container to reveal the currently active tab because that makes it very
// hard to close multiple inactive tabs next to each other.
delegatingEditorService.setEditorCloseHandler((position, editor) => {
const group = stacks.groupAt(position);
if (group && stacks.isActive(group) && !group.isActive(editor)) {
this.editorGroupService.focusGroup(group);
}
this.blockRevealActiveTab = true;
return TPromise.as(void 0);
});
return this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService]));
}
public create(parent: HTMLElement): void {
super.create(parent);

View file

@ -122,8 +122,6 @@ export class NotificationsToasts extends Themable {
this.notificationsToastsContainer.appendChild(notificationToastContainer);
}
itemDisposeables.push(toDisposable(() => this.notificationsToastsContainer.removeChild(notificationToastContainer)));
// Toast
const notificationToast = document.createElement('div');
addClass(notificationToast, 'notification-toast');
@ -135,7 +133,15 @@ export class NotificationsToasts extends Themable {
verticalScrollMode: ScrollbarVisibility.Hidden
});
itemDisposeables.push(notificationList);
this.mapNotificationToToast.set(item, { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, disposeables: itemDisposeables });
const toast: INotificationToast = { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, disposeables: itemDisposeables };
this.mapNotificationToToast.set(item, toast);
itemDisposeables.push(toDisposable(() => {
if (this.isVisible(toast)) {
this.notificationsToastsContainer.removeChild(toast.container);
}
}));
// Make visible
notificationList.show();

View file

@ -206,7 +206,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
currentDecoration = !!message ? Severity.Error : void 0;
const newPick = message || defaultMessage;
if (newPick !== currentPick) {
options.valueSelection = [lastValue.length, lastValue.length];
options.valueSelection = null;
currentPick = newPick;
resolve(new TPromise<any>(init));
}

View file

@ -238,23 +238,23 @@ export class WorkbenchShell {
/* __GDPR__
"workspaceLoad" : {
"userAgent" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"windowSize.innerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"windowSize.innerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"windowSize.outerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"windowSize.outerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"emptyWorkbench": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"workbench.filesToOpen": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"workbench.filesToCreate": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"workbench.filesToDiff": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"customKeybindingsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"windowSize.innerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"windowSize.innerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"windowSize.outerHeight": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"windowSize.outerWidth": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"emptyWorkbench": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbench.filesToOpen": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbench.filesToCreate": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"workbench.filesToDiff": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"customKeybindingsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"theme": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"language": { "classification": "SystemMetaData", "purpose": "BusinessInsight" },
"experiments": { "${inline}": [ "${IExperiments}" ] },
"pinnedViewlets": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"restoredViewlet": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"restoredEditors": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"restoredEditors": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"pinnedViewlets": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('workspaceLoad', {
@ -312,14 +312,14 @@ export class WorkbenchShell {
perf.mark('didStatLocalStorage');
/* __GDPR__
"localStorageTimers" : {
"statTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"accessTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"firstReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"subsequentReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"writeTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"keys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"size": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"localStorageTimers2" : {
"statTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"accessTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"firstReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"subsequentReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"writeTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"keys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"size": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('localStorageTimers2', {

View file

@ -1319,13 +1319,11 @@ export class Workbench implements IPartService {
// Check if zen mode transitioned to full screen and if now we are out of zen mode -> we need to go out of full screen
let toggleFullScreen = false;
// Same goes for the centered editor layout
let toggleCenteredEditorLayout = false;
if (this.zenMode.active) {
const config = this.configurationService.getValue<IZenModeSettings>('zenMode');
toggleFullScreen = !browser.isFullscreen() && config.fullScreen;
this.zenMode.transitionedToFullScreen = toggleFullScreen;
toggleCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout;
this.zenMode.transitionedToCenteredEditorLayout = toggleCenteredEditorLayout;
this.zenMode.transitionedToCenteredEditorLayout = !this.isEditorLayoutCentered() && config.centerLayout;
this.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART);
this.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART);
this.setPanelHidden(true, true).done(void 0, errors.onUnexpectedError);
@ -1342,14 +1340,20 @@ export class Workbench implements IPartService {
if (config.hideTabs) {
this.editorPart.hideTabs(true);
}
if (config.centerLayout) {
this.centerEditorLayout(true, true);
}
} else {
if (this.zenMode.wasPanelVisible) {
this.setPanelHidden(false, true).done(void 0, errors.onUnexpectedError);
}
if (this.zenMode.wasSideBarVisible) {
this.setSideBarHidden(false, true).done(void 0, errors.onUnexpectedError);
}
if (this.zenMode.transitionedToCenteredEditorLayout) {
this.centerEditorLayout(false, true);
}
// Status bar and activity bar visibility come from settings -> update their visibility.
this.onDidUpdateConfiguration(true);
@ -1360,15 +1364,10 @@ export class Workbench implements IPartService {
}
toggleFullScreen = this.zenMode.transitionedToFullScreen && browser.isFullscreen();
toggleCenteredEditorLayout = this.zenMode.transitionedToCenteredEditorLayout && this.isEditorLayoutCentered();
}
this.inZenMode.set(this.zenMode.active);
if (toggleCenteredEditorLayout) {
this.toggleCenteredEditorLayout(true);
}
if (!skipLayout) {
this.layout();
}
@ -1382,8 +1381,8 @@ export class Workbench implements IPartService {
return this.centeredEditorLayoutActive;
}
public toggleCenteredEditorLayout(skipLayout?: boolean): void {
this.centeredEditorLayoutActive = !this.centeredEditorLayoutActive;
public centerEditorLayout(active: boolean, skipLayout?: boolean): void {
this.centeredEditorLayoutActive = active;
this.storageService.store(Workbench.centeredEditorLayoutActiveStorageKey, this.centeredEditorLayoutActive, StorageScope.GLOBAL);
if (!skipLayout) {

View file

@ -49,9 +49,9 @@ export class NodeCachedDataManager implements IWorkbenchContribution {
// log summary
/* __GDPR__
"cachedDataInfo" : {
"didRequestCachedData" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"didRejectCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"didProduceCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
"didRequestCachedData" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"didRejectCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"didProduceCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
this._telemetryService.publicLog('cachedDataInfo', {

View file

@ -23,6 +23,8 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { CollapseAction } from 'vs/workbench/browser/viewlet';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { first } from 'vs/base/common/arrays';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
export abstract class AbstractDebugAction extends Action {
@ -116,7 +118,8 @@ export class StartAction extends AbstractDebugAction {
constructor(id: string, label: string,
@IDebugService debugService: IDebugService,
@IKeybindingService keybindingService: IKeybindingService,
@IWorkspaceContextService private contextService: IWorkspaceContextService
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IHistoryService private historyService: IHistoryService
) {
super(id, label, 'debug-action start', debugService, keybindingService);
@ -126,7 +129,19 @@ export class StartAction extends AbstractDebugAction {
}
public run(): TPromise<any> {
const launch = this.debugService.getConfigurationManager().selectedConfiguration.launch;
const configurationManager = this.debugService.getConfigurationManager();
let launch = configurationManager.selectedConfiguration.launch;
if (!launch) {
const rootUri = this.historyService.getLastActiveWorkspaceRoot();
launch = configurationManager.getLaunch(rootUri);
if (!launch) {
const launches = configurationManager.getLaunches();
launch = first(launches, l => !!l.getConfigurationNames().length, launches.length ? launches[0] : undefined);
}
configurationManager.selectConfiguration(launch);
}
return this.debugService.startDebugging(launch, undefined, this.isNoDebug());
}

View file

@ -34,13 +34,12 @@ export function registerCommands(): void {
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.lastFocusedList;
// Tree only
if (!(focused instanceof List)) {
const tree = focused;
const element = <IEnablement>tree.getFocus();
debugService.enableOrDisableBreakpoints(!element.enabled, element).done(null, errors.onUnexpectedError);
const list = listService.lastFocusedList;
if (list instanceof List) {
const focused = <IEnablement[]>list.getFocusedElements();
if (focused && focused.length) {
debugService.enableOrDisableBreakpoints(!focused[0].enabled, focused[0]).done(null, errors.onUnexpectedError);
}
}
}
});
@ -111,17 +110,18 @@ export function registerCommands(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.removeBreakpoint',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_BREAKPOINT_SELECTED.toNegated()),
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_SELECTED.toNegated()),
primary: KeyCode.Delete,
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.lastFocusedList;
const list = listService.lastFocusedList;
// Tree only
if (!(focused instanceof List)) {
const element = focused.getFocus();
if (list instanceof List) {
const focused = list.getFocusedElements();
const element = focused.length ? focused[0] : undefined;
if (element instanceof Breakpoint) {
debugService.removeBreakpoints(element.getId()).done(null, errors.onUnexpectedError);
} else if (element instanceof FunctionBreakpoint) {

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}.icon-white{fill:#fff;}</style></defs><title>breakpoint-conditional-disabled</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,8A6,6,0,1,1,8,2,6.007,6.007,0,0,1,14,8Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M13,8A5,5,0,1,1,8,3,5,5,0,0,1,13,8Z"/><path class="icon-white" d="M11,6V7H5V6ZM5,10h6V9H5Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-conditional-disabled</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,8A5,5,0,1,1,8,3,5.006,5.006,0,0,1,13,8Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M8,4a4,4,0,1,0,4,4A4,4,0,0,0,8,4Zm2,6H6V9h4Zm0-3H6V6h4Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 627 B

After

Width:  |  Height:  |  Size: 569 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-conditional-unverified</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,8A6,6,0,1,1,8,2,6.007,6.007,0,0,1,14,8Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M8,3a5,5,0,1,0,5,5A5,5,0,0,0,8,3Zm0,9a4,4,0,1,1,4-4A4,4,0,0,1,8,12ZM5.5,9h5v1h-5Zm0-3h5V7h-5Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-conditional-unverified</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,8A5,5,0,1,1,8,3,5.006,5.006,0,0,1,13,8Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M8,4a4,4,0,1,0,4,4A4,4,0,0,0,8,4Zm0,7a3,3,0,1,1,3-3A3,3,0,0,1,8,11ZM6.5,8.5h3v1h-3Zm0-2h3v1h-3Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 609 B

After

Width:  |  Height:  |  Size: 611 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-red{fill:#e51400;}.icon-white{fill:#fff;}</style></defs><title>breakpoint-conditional</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,8A6,6,0,1,1,8,2,6.007,6.007,0,0,1,14,8Z"/></g><g id="iconBg"><path class="icon-vs-red" d="M13,8A5,5,0,1,1,8,3,5,5,0,0,1,13,8Z"/><path class="icon-white" d="M11,6V7H5V6ZM5,10h6V9H5Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-red{fill:#e51400;}</style></defs><title>breakpoint-conditional</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,8A5,5,0,1,1,8,3,5.006,5.006,0,0,1,13,8Z"/></g><g id="iconBg"><path class="icon-vs-red" d="M8,4a4,4,0,1,0,4,4A4,4,0,0,0,8,4Zm2,6H6V9h4Zm0-3H6V6h4Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 546 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-disabled</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,8A6,6,0,1,1,8,2,6.007,6.007,0,0,1,14,8Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M13,8A5,5,0,1,1,8,3,5,5,0,0,1,13,8Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-disabled</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,8A5,5,0,1,1,8,3,5.006,5.006,0,0,1,13,8Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M12,8A4,4,0,1,1,8,4,4,4,0,0,1,12,8Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 537 B

After

Width:  |  Height:  |  Size: 537 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-function-disabled</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M13,13H3L8,4Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-function-disabled</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13.7,13H2.3L8,2.741Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M12,12H4L8,4.8Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 502 B

After

Width:  |  Height:  |  Size: 504 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-function-unverified</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M8,4,3,13H13ZM8,6.059,11.3,12H4.7Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-disabled-grey{fill:#848484;}</style></defs><title>breakpoint-function-unverified</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13.7,13H2.3L8,2.741Z"/></g><g id="iconBg"><path class="icon-disabled-grey" d="M8,4.8,4,12h8ZM8,6.859,10.3,11H5.7Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 526 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-red{fill:#e51400;}</style></defs><title>breakpoint-function</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.7,14H1.3L8,1.941Z"/></g><g id="iconBg"><path class="icon-vs-red" d="M13,13H3L8,4Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-red{fill:#e51400;}</style></defs><title>breakpoint-function</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13.7,13H2.3L8,2.741Z"/></g><g id="iconBg"><path class="icon-vs-red" d="M12,12H4L8,4.8Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 479 B

After

Width:  |  Height:  |  Size: 481 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.cls-1{fill:#e51400;fill-opacity:0.4;}</style></defs><title>breakpoint-hint</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M13,8A5,5,0,1,1,8,3,5.006,5.006,0,0,1,13,8Z"/></g><g id="iconBg"><path class="cls-1" d="M12,8A4,4,0,1,1,8,4,4,4,0,0,1,12,8Z"/></g></svg>

After

Width:  |  Height:  |  Size: 524 B

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