Merge branch 'master' into isidorn/multiRootDnD
2
.github/calendar.yml
vendored
|
@ -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',
|
||||
}
|
|
@ -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"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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/') },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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"] }
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
89
extensions/json/server/src/jsonFolding.ts
Normal 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 };
|
||||
}
|
|
@ -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
|
||||
|
|
109
extensions/json/server/src/test/folding.test.ts
Normal 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')]);
|
||||
});
|
||||
|
||||
});
|
|
@ -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');
|
||||
}
|
||||
|
|
3
extensions/json/server/test/mocha.opts
Normal file
|
@ -0,0 +1,3 @@
|
|||
--ui tdd
|
||||
--useColors true
|
||||
./out/test/**/*.test.js
|
|
@ -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"
|
||||
|
|
|
@ -300,6 +300,7 @@
|
|||
"keyword.operator.expression",
|
||||
"keyword.operator.cast",
|
||||
"keyword.operator.sizeof",
|
||||
"keyword.operator.instanceof",
|
||||
"keyword.operator.logical.python"
|
||||
],
|
||||
"settings": {
|
||||
|
|
|
@ -319,6 +319,7 @@
|
|||
"keyword.operator.expression",
|
||||
"keyword.operator.cast",
|
||||
"keyword.operator.sizeof",
|
||||
"keyword.operator.instanceof",
|
||||
"keyword.operator.logical.python"
|
||||
],
|
||||
"settings": {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 += '?';
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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);
|
||||
|
|
13
src/typings/vscode-xterm.d.ts
vendored
|
@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) } }
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ class TestHeightMap extends HeightMap {
|
|||
return {
|
||||
model: item,
|
||||
top: 0,
|
||||
height: item.getHeight()
|
||||
height: item.getHeight(),
|
||||
width: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
* / \ / \
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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, {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}"
|
||||
]
|
||||
|
|
|
@ -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" }
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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.")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -11,7 +11,6 @@ import { deepClone } from 'vs/base/common/objects';
|
|||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"IExperiments" : {
|
||||
"deployToAzureQuickLink" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
export interface IExperiments {
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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
|
@ -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.
|
||||
|
|
3
src/vs/vscode.proposed.d.ts
vendored
|
@ -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>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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', {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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', {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |