Merge branch 'master' into gitattributes

This commit is contained in:
Joao Moreno 2018-03-07 08:56:26 +01:00
commit 0dfe0c4c3e
208 changed files with 16826 additions and 2944 deletions

View file

@ -38,5 +38,6 @@
}
}
],
"typescript.tsdk": "node_modules/typescript/lib"
}
"typescript.tsdk": "node_modules/typescript/lib",
"npm.exclude": "**/extensions/**"
}

View file

@ -81,7 +81,8 @@ const indentationFilter = [
'!build/{lib,tslintRules}/**/*.js',
'!build/**/*.sh',
'!build/tfs/**/*.js',
'!**/Dockerfile'
'!**/Dockerfile',
'!extensions/markdown/media/*.js'
];
const copyrightFilter = [

View file

@ -78,7 +78,7 @@ const vscodeResources = [
'out-build/vs/workbench/electron-browser/bootstrap/**',
'out-build/vs/workbench/parts/debug/**/*.json',
'out-build/vs/workbench/parts/execution/**/*.scpt',
'out-build/vs/workbench/parts/html/browser/webview-pre.js',
'out-build/vs/workbench/parts/html/electron-browser/webview-pre.js',
'out-build/vs/**/markdown.css',
'out-build/vs/workbench/parts/tasks/**/*.json',
'out-build/vs/workbench/parts/terminal/electron-browser/terminalProcess.js',

View file

@ -134,6 +134,10 @@
"name": "vs/workbench/parts/watermark",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/parts/webview",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/parts/welcome",
"project": "vscode-workbench"

View file

@ -24,6 +24,13 @@ interface ExpandAbbreviationInput {
filter?: string;
}
interface PreviewRangesWithContent {
previewRange: vscode.Range;
originalRange: vscode.Range;
originalContent: string;
textToWrapInPreview: string[];
}
export function wrapWithAbbreviation(args: any) {
if (!validate(false) || !vscode.window.activeTextEditor) {
return;
@ -32,50 +39,145 @@ export function wrapWithAbbreviation(args: any) {
const editor = vscode.window.activeTextEditor;
let rootNode = parseDocument(editor.document, false);
const syntax = getSyntaxFromArgs({ language: editor.document.languageId });
const syntax = getSyntaxFromArgs({ language: editor.document.languageId }) || '';
if (!syntax) {
return;
}
const abbreviationPromise = (args && args['abbreviation']) ? Promise.resolve(args['abbreviation']) : vscode.window.showInputBox({ prompt: 'Enter Abbreviation' });
let inPreview = false;
// Fetch general information for the succesive expansions. i.e. the ranges to replace and its contents
let rangesToReplace: PreviewRangesWithContent[] = [];
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) {
let { active } = selection;
let currentNode = getNode(rootNode, active, true);
if (currentNode && (currentNode.start.line === active.line || currentNode.end.line === active.line)) {
rangeToReplace = new vscode.Range(currentNode.start, currentNode.end);
} else {
rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, editor.document.lineAt(rangeToReplace.start.line).text.length);
}
}
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);
const wholeFirstLine = editor.document.lineAt(rangeToReplace.start).text;
const otherMatches = wholeFirstLine.match(/^(\s*)/);
const preceedingWhiteSpace = otherMatches ? otherMatches[1] : '';
let textToReplace = editor.document.getText(rangeToReplace);
let textToWrapInPreview = rangeToReplace.isSingleLine ? [textToReplace] : ['\n\t' + textToReplace.split('\n' + preceedingWhiteSpace).join('\n\t') + '\n'];
rangesToReplace.push({ previewRange: rangeToReplace, originalRange: rangeToReplace, originalContent: textToReplace, textToWrapInPreview });
});
let abbreviationPromise;
let currentValue = '';
function inputChanged(value: string): string {
if (value !== currentValue) {
currentValue = value;
makeChanges(value, inPreview, false).then((out) => {
if (typeof out === 'boolean') {
inPreview = out;
}
});
}
return '';
}
abbreviationPromise = (args && args['abbreviation']) ? Promise.resolve(args['abbreviation']) : vscode.window.showInputBox({ prompt: 'Enter Abbreviation', validateInput: inputChanged });
const helper = getEmmetHelper();
return abbreviationPromise.then(inputAbbreviation => {
if (!inputAbbreviation || !inputAbbreviation.trim() || !helper.isAbbreviationValid(syntax, inputAbbreviation)) { return false; }
function makeChanges(inputAbbreviation: string | undefined, inPreview: boolean, definitive: boolean): Thenable<boolean> {
if (!inputAbbreviation || !inputAbbreviation.trim() || !helper.isAbbreviationValid(syntax, inputAbbreviation)) {
return inPreview ? revertPreview(editor, rangesToReplace).then(() => { return false; }) : Promise.resolve(inPreview);
}
let extractedResults = helper.extractAbbreviationFromText(inputAbbreviation);
if (!extractedResults) {
return false;
return Promise.resolve(inPreview);
} else if (extractedResults.abbreviation !== inputAbbreviation) {
// Not clear what should we do in this case. Warn the user? How?
}
let { abbreviation, filter } = extractedResults;
if (definitive) {
const revertPromise = inPreview ? revertPreview(editor, rangesToReplace) : Promise.resolve();
return revertPromise.then(() => {
const expandAbbrList: ExpandAbbreviationInput[] = rangesToReplace.map(rangesAndContent => {
let rangeToReplace = rangesAndContent.originalRange;
let textToWrap = rangeToReplace.isSingleLine ? ['$TM_SELECTED_TEXT'] : ['\n\t$TM_SELECTED_TEXT\n'];
return { syntax, abbreviation, rangeToReplace, textToWrap, filter };
});
return expandAbbreviationInRange(editor, expandAbbrList, true).then(() => { return true; });
});
}
let expandAbbrList: ExpandAbbreviationInput[] = [];
editor.selections.forEach(selection => {
let rangeToReplace: vscode.Range = selection.isReversed ? new vscode.Range(selection.active, selection.anchor) : selection;
if (rangeToReplace.isEmpty) {
let { active } = selection;
let currentNode = getNode(rootNode, active, true);
if (currentNode && (currentNode.start.line === active.line || currentNode.end.line === active.line)) {
rangeToReplace = new vscode.Range(currentNode.start, currentNode.end);
} else {
rangeToReplace = new vscode.Range(rangeToReplace.start.line, 0, rangeToReplace.start.line, editor.document.lineAt(rangeToReplace.start.line).text.length);
}
}
const firstLineOfSelection = editor.document.lineAt(rangeToReplace.start).text.substr(rangeToReplace.start.character);
const matches = firstLineOfSelection.match(/^(\s*)/);
const preceedingWhiteSpace = matches ? matches[1].length : 0;
rangeToReplace = new vscode.Range(rangeToReplace.start.line, rangeToReplace.start.character + preceedingWhiteSpace, rangeToReplace.end.line, rangeToReplace.end.character);
let textToWrap = rangeToReplace.isSingleLine ? ['$TM_SELECTED_TEXT'] : ['\n\t$TM_SELECTED_TEXT\n'];
expandAbbrList.push({ syntax, abbreviation, rangeToReplace, textToWrap, filter });
const expandAbbrList: ExpandAbbreviationInput[] = rangesToReplace.map(rangesAndContent => {
return { syntax, abbreviation, rangeToReplace: rangesAndContent.originalRange, textToWrap: rangesAndContent.textToWrapInPreview, filter };
});
return expandAbbreviationInRange(editor, expandAbbrList, true);
return applyPreview(editor, expandAbbrList, rangesToReplace);
}
// On inputBox closing
return abbreviationPromise.then(inputAbbreviation => {
return makeChanges(inputAbbreviation, inPreview, true);
});
}
function revertPreview(editor: vscode.TextEditor, rangesToReplace: PreviewRangesWithContent[]): Thenable<any> {
return editor.edit(builder => {
for (let i = 0; i < rangesToReplace.length; i++) {
builder.replace(rangesToReplace[i].previewRange, rangesToReplace[i].originalContent);
rangesToReplace[i].previewRange = rangesToReplace[i].originalRange;
}
}, { undoStopBefore: false, undoStopAfter: false });
}
function applyPreview(editor: vscode.TextEditor, expandAbbrList: ExpandAbbreviationInput[], rangesToReplace: PreviewRangesWithContent[]): Thenable<boolean> {
let totalLinesInserted = 0;
return editor.edit(builder => {
for (let i = 0; i < rangesToReplace.length; i++) {
const expandedText = expandAbbr(expandAbbrList[i]) || '';
if (!expandedText) {
// Failed to expand text. We already showed an error inside expandAbbr.
break;
}
const oldPreviewRange = rangesToReplace[i].previewRange;
const preceedingText = editor.document.getText(new vscode.Range(oldPreviewRange.start.line, 0, oldPreviewRange.start.line, oldPreviewRange.start.character));
const indentPrefix = (preceedingText.match(/^(\s*)/) || ['', ''])[1];
let newText = expandedText.replace(/\n/g, '\n' + indentPrefix); // Adding indentation on each line of expanded text
newText = newText.replace(/\$\{[\d]*\}/g, '|'); // Removing Tabstops
newText = newText.replace(/\$\{[\d]*(:[^}]*)?\}/g, (match) => { // Replacing Placeholders
return match.replace(/^\$\{[\d]*:/, '').replace('}', '');
});
builder.replace(oldPreviewRange, newText);
const expandedTextLines = newText.split('\n');
const oldPreviewLines = oldPreviewRange.end.line - oldPreviewRange.start.line + 1;
const newLinesInserted = expandedTextLines.length - oldPreviewLines;
let lastLineEnd = expandedTextLines[expandedTextLines.length - 1].length;
if (expandedTextLines.length === 1) {
// If the expandedText is single line, add the length of preceeding whitespace as it will not be included in line length.
lastLineEnd += oldPreviewRange.start.character;
}
rangesToReplace[i].previewRange = new vscode.Range(oldPreviewRange.start.line + totalLinesInserted, oldPreviewRange.start.character, oldPreviewRange.end.line + totalLinesInserted + newLinesInserted, lastLineEnd);
totalLinesInserted += newLinesInserted;
}
}, { undoStopBefore: false, undoStopAfter: false });
}
export function wrapIndividualLinesWithAbbreviation(args: any) {
if (!validate(false) || !vscode.window.activeTextEditor) {
return;

View file

@ -309,7 +309,7 @@ export function sameNodes(node1: Node, node2: Node): boolean {
export function getEmmetConfiguration(syntax: string) {
const emmetConfig = vscode.workspace.getConfiguration('emmet');
const syntaxProfiles = Object.assign({}, emmetConfig['syntaxProfiles'] || {});
const preferences = Object.assign({}, emmetConfig['preferences'] || {});
// jsx, xml and xsl syntaxes need to have self closing tags unless otherwise configured by user
if (syntax === 'jsx' || syntax === 'xml' || syntax === 'xsl') {
syntaxProfiles[syntax] = syntaxProfiles[syntax] || {};
@ -322,7 +322,7 @@ export function getEmmetConfiguration(syntax: string) {
}
return {
preferences: emmetConfig['preferences'],
preferences,
showExpandedAbbreviation: emmetConfig['showExpandedAbbreviation'],
showAbbreviationSuggestions: emmetConfig['showAbbreviationSuggestions'],
syntaxProfiles,

View file

@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { applyEdits } from '../utils/edits';
import { TextDocument, TextEdit, Position, Range } from 'vscode-languageserver-types';
suite('Edits', () => {
test('inserts', function (): any {
let input = TextDocument.create('foo://bar/f', 'html', 0, '012345678901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.insert(Position.create(0, 0), 'Hello')]), 'Hello012345678901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.insert(Position.create(0, 1), 'Hello')]), '0Hello12345678901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.insert(Position.create(0, 1), 'Hello'), TextEdit.insert(Position.create(0, 1), 'World')]), '0HelloWorld12345678901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.insert(Position.create(0, 2), 'One'), TextEdit.insert(Position.create(0, 1), 'Hello'), TextEdit.insert(Position.create(0, 1), 'World'), TextEdit.insert(Position.create(0, 2), 'Two'), TextEdit.insert(Position.create(0, 2), 'Three')]), '0HelloWorld1OneTwoThree2345678901234567890123456789');
});
test('replace', function (): any {
let input = TextDocument.create('foo://bar/f', 'html', 0, '012345678901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.replace(Range.create(Position.create(0, 3), Position.create(0, 6)), 'Hello')]), '012Hello678901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.replace(Range.create(Position.create(0, 3), Position.create(0, 6)), 'Hello'), TextEdit.replace(Range.create(Position.create(0, 6), Position.create(0, 9)), 'World')]), '012HelloWorld901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.replace(Range.create(Position.create(0, 3), Position.create(0, 6)), 'Hello'), TextEdit.insert(Position.create(0, 6), 'World')]), '012HelloWorld678901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.insert(Position.create(0, 6), 'World'), TextEdit.replace(Range.create(Position.create(0, 3), Position.create(0, 6)), 'Hello')]), '012HelloWorld678901234567890123456789');
assert.equal(applyEdits(input, [TextEdit.insert(Position.create(0, 3), 'World'), TextEdit.replace(Range.create(Position.create(0, 3), Position.create(0, 6)), 'Hello')]), '012WorldHello678901234567890123456789');
});
test('overlap', function (): any {
let input = TextDocument.create('foo://bar/f', 'html', 0, '012345678901234567890123456789');
assert.throws(_ => applyEdits(input, [TextEdit.replace(Range.create(Position.create(0, 3), Position.create(0, 6)), 'Hello'), TextEdit.insert(Position.create(0, 3), 'World')]));
assert.throws(_ => applyEdits(input, [TextEdit.replace(Range.create(Position.create(0, 3), Position.create(0, 6)), 'Hello'), TextEdit.insert(Position.create(0, 4), 'World')]));
});
});

View file

@ -14,4 +14,46 @@ export function pushAll<T>(to: T[], from: T[]) {
export function contains<T>(arr: T[], val: T) {
return arr.indexOf(val) !== -1;
}
}
/**
* Like `Array#sort` but always stable. Usually runs a little slower `than Array#sort`
* so only use this when actually needing stable sort.
*/
export function mergeSort<T>(data: T[], compare: (a: T, b: T) => number): T[] {
_divideAndMerge(data, compare);
return data;
}
function _divideAndMerge<T>(data: T[], compare: (a: T, b: T) => number): void {
if (data.length <= 1) {
// sorted
return;
}
const p = (data.length / 2) | 0;
const left = data.slice(0, p);
const right = data.slice(p);
_divideAndMerge(left, compare);
_divideAndMerge(right, compare);
let leftIdx = 0;
let rightIdx = 0;
let i = 0;
while (leftIdx < left.length && rightIdx < right.length) {
let ret = compare(left[leftIdx], right[rightIdx]);
if (ret <= 0) {
// smaller_equal -> take left to preserve order
data[i++] = left[leftIdx++];
} else {
// greater -> take right
data[i++] = right[rightIdx++];
}
}
while (leftIdx < left.length) {
data[i++] = left[leftIdx++];
}
while (rightIdx < right.length) {
data[i++] = right[rightIdx++];
}
}

View file

@ -4,29 +4,29 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TextDocument, TextEdit, Position } from 'vscode-languageserver-types';
import { TextDocument, TextEdit } from 'vscode-languageserver-types';
import { mergeSort } from './arrays';
export function applyEdits(document: TextDocument, edits: TextEdit[]): string {
let text = document.getText();
let sortedEdits = edits.sort((a, b) => {
let startDiff = comparePositions(a.range.start, b.range.start);
if (startDiff === 0) {
return comparePositions(a.range.end, b.range.end);
let sortedEdits = mergeSort(edits, (a, b) => {
let diff = a.range.start.line - b.range.start.line;
if (diff === 0) {
return a.range.start.character - b.range.start.character;
}
return startDiff;
return 0;
});
sortedEdits.forEach(e => {
let lastModifiedOffset = text.length;
for (let i = sortedEdits.length - 1; i >= 0; i--) {
let e = sortedEdits[i];
let startOffset = document.offsetAt(e.range.start);
let endOffset = document.offsetAt(e.range.end);
text = text.substring(0, startOffset) + e.newText + text.substring(endOffset, text.length);
});
return text;
}
function comparePositions(p1: Position, p2: Position) {
let diff = p2.line - p1.line;
if (diff === 0) {
return p2.character - p1.character;
if (endOffset <= lastModifiedOffset) {
text = text.substring(0, startOffset) + e.newText + text.substring(endOffset, text.length);
} else {
throw new Error('Ovelapping edit');
}
lastModifiedOffset = startOffset;
}
return diff;
return text;
}

View file

@ -1,7 +1,7 @@
{
"name": "json",
"displayName": "%displayName%",
"description": "%description%",
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
@ -171,7 +171,7 @@
},
"dependencies": {
"vscode-extension-telemetry": "0.0.15",
"vscode-languageclient": "4.0.0-next.9",
"vscode-languageclient": "^4.0.0",
"vscode-nls": "^3.2.1"
},
"devDependencies": {

View file

@ -8,10 +8,10 @@
"node": "*"
},
"dependencies": {
"jsonc-parser": "^1.0.1",
"jsonc-parser": "^1.0.2",
"request-light": "^0.2.2",
"vscode-json-languageservice": "^3.0.7",
"vscode-languageserver": "4.0.0-next.3",
"vscode-json-languageservice": "^3.0.8",
"vscode-languageserver": "^4.0.0",
"vscode-nls": "^3.2.1",
"vscode-uri": "^1.0.1"
},

View file

@ -7,11 +7,9 @@
import {
createConnection, IConnection,
TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, DocumentColorRequest, ColorPresentationRequest,
} from 'vscode-languageserver';
import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';
import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light';
import fs = require('fs');
import URI from 'vscode-uri';
@ -74,7 +72,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
clientSnippetSupport = hasClientCapability('textDocument', 'completion', 'completionItem', 'snippetSupport');
clientDynamicRegisterSupport = hasClientCapability('workspace', 'symbol', 'dynamicRegistration');
let capabilities: ServerCapabilities & CPServerCapabilities & FoldingProviderServerCapabilities = {
let capabilities: ServerCapabilities & FoldingProviderServerCapabilities = {
// Tell the client that the server works in FULL text document sync mode
textDocumentSync: documents.syncKind,
completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : void 0,

View file

@ -48,9 +48,9 @@ https-proxy-agent@2.1.1:
agent-base "^4.1.0"
debug "^3.1.0"
jsonc-parser@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-1.0.1.tgz#7f8f296414e6e7c4a33b9e4914fc8c47e4421675"
jsonc-parser@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-1.0.2.tgz#3fe86c0237db206fe5693872d93eec3bc3b05055"
ms@2.0.0:
version "2.0.0"
@ -64,35 +64,35 @@ request-light@^0.2.2:
https-proxy-agent "2.1.1"
vscode-nls "^2.0.2"
vscode-json-languageservice@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.0.7.tgz#dc00117d51d4a7ac3bde9204afa701f962f00736"
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"
dependencies:
jsonc-parser "^1.0.1"
vscode-languageserver-types "^3.6.0-next.1"
vscode-nls "^2.0.2"
jsonc-parser "^1.0.2"
vscode-languageserver-types "^3.6.0"
vscode-nls "^3.2.1"
vscode-uri "^1.0.1"
vscode-jsonrpc@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe"
vscode-jsonrpc@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0.tgz#848d56995d5168950d84feb5d9c237ae5c6a02d4"
vscode-languageserver-protocol@^3.6.0-next.3:
version "3.6.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.6.0-next.4.tgz#5b9940e4d6afafd5b63f9731dbd3a9bcc65b3719"
vscode-languageserver-protocol@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.6.0.tgz#579642cdcccf74b0cd771c33daa3239acb40d040"
dependencies:
vscode-jsonrpc "^3.6.0-next.1"
vscode-languageserver-types "^3.6.0-next.1"
vscode-jsonrpc "^3.6.0"
vscode-languageserver-types "^3.6.0"
vscode-languageserver-types@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"
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@4.0.0-next.3:
version "4.0.0-next.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0-next.3.tgz#89a9ce5078e3a86a78e3551c3766194ce4295611"
vscode-languageserver@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0.tgz#8b792f0d6d10acfe363d02371ed4ce53d08af88a"
dependencies:
vscode-languageserver-protocol "^3.6.0-next.3"
vscode-languageserver-protocol "^3.6.0"
vscode-uri "^1.0.1"
vscode-nls@^2.0.2:

View file

@ -34,26 +34,26 @@ vscode-extension-telemetry@0.0.15:
dependencies:
applicationinsights "1.0.1"
vscode-jsonrpc@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe"
vscode-jsonrpc@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0.tgz#848d56995d5168950d84feb5d9c237ae5c6a02d4"
vscode-languageclient@4.0.0-next.9:
version "4.0.0-next.9"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0-next.9.tgz#2a06568f46ee9de3490f85e227d3740a21a03d3a"
vscode-languageclient@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0.tgz#635f5bfbcfa1385dae489b394857f1db8b459a7d"
dependencies:
vscode-languageserver-protocol "^3.6.0-next.5"
vscode-languageserver-protocol "^3.6.0"
vscode-languageserver-protocol@^3.6.0-next.5:
version "3.6.0-next.5"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.6.0-next.5.tgz#ed2ec2db759826f753c0a13977dfb2bedc4d31b3"
vscode-languageserver-protocol@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.6.0.tgz#579642cdcccf74b0cd771c33daa3239acb40d040"
dependencies:
vscode-jsonrpc "^3.6.0-next.1"
vscode-languageserver-types "^3.6.0-next.1"
vscode-jsonrpc "^3.6.0"
vscode-languageserver-types "^3.6.0"
vscode-languageserver-types@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"
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-nls@^3.2.1:
version "3.2.1"

View file

@ -26,6 +26,7 @@
"name": "lessc",
"label": "Lessc compiler",
"owner": "lessc",
"source": "less",
"fileLocation": "absolute",
"pattern": {
"regexp": "(.*)\\sin\\s(.*)\\son line\\s(\\d+),\\scolumn\\s(\\d+)",

View file

@ -1,49 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
(function () {
const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings'));
const strings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-strings'));
let didShow = false;
const showCspWarning = () => {
if (didShow || settings.disableSecurityWarnings) {
return;
}
didShow = true;
const notification = document.createElement('a');
notification.innerText = strings.cspAlertMessageText;
notification.setAttribute('id', 'code-csp-warning');
notification.setAttribute('title', strings.cspAlertMessageTitle);
notification.setAttribute('role', 'button');
notification.setAttribute('aria-label', strings.cspAlertMessageLabel);
notification.onclick = () => {
window.parent.postMessage({
type: 'command',
source: settings.source,
body: {
command: 'markdown.showPreviewSecuritySelector',
args: [settings.source]
}
}, '*');
};
document.body.appendChild(notification);
};
document.addEventListener('securitypolicyviolation', () => {
showCspWarning();
});
window.addEventListener('message', (event) => {
if (event && event.data && event.data.name === 'vscode-did-block-svg') {
showCspWarning();
}
});
}());

File diff suppressed because one or more lines are too long

View file

@ -1,40 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
'use strict';
(function () {
const unloadedStyles = [];
const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings'));
const onStyleLoadError = (event) => {
const source = event.target.dataset.source;
unloadedStyles.push(source);
};
window.addEventListener('DOMContentLoaded', () => {
for (const link of document.getElementsByClassName('code-user-style')) {
if (link.dataset.source) {
link.onerror = onStyleLoadError;
}
}
})
window.addEventListener('load', () => {
if (!unloadedStyles.length) {
return;
}
window.parent.postMessage({
type: 'command',
source: settings.source,
body: {
command: '_markdown.onPreviewStyleLoadError',
args: [unloadedStyles]
}
}, '*');
});
}());

View file

@ -1,344 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
'use strict';
(function () {
// From https://remysharp.com/2010/07/21/throttling-function-calls
function throttle(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last, deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold + last - now);
} else {
last = now;
fn.apply(context, args);
}
};
}
/**
* @param {number} min
* @param {number} max
* @param {number} value
*/
function clamp(min, max, value) {
return Math.min(max, Math.max(min, value));
}
/**
* @param {number} line
*/
function clampLine(line) {
return clamp(0, settings.lineCount - 1, line);
}
/**
* Post a message to the markdown extension
*
* @param {string} type
* @param {object} body
*/
function postMessage(type, body) {
window.parent.postMessage({
type,
source: settings.source,
body
}, '*');
}
/**
* Post a command to be executed to the markdown extension
*
* @param {string} command
* @param {any[]} args
*/
function postCommand(command, args) {
postMessage('command', { command, args });
}
/**
* @typedef {{ element: Element, line: number }} CodeLineElement
*/
/**
* @return {CodeLineElement[]}
*/
const getCodeLineElements = (() => {
/** @type {CodeLineElement[]} */
let elements;
return () => {
if (!elements) {
elements = Array.prototype.map.call(
document.getElementsByClassName('code-line'),
element => {
const line = +element.getAttribute('data-line');
return { element, line }
})
.filter(x => !isNaN(x.line));
}
return elements;
};
})()
/**
* Find the html elements that map to a specific target line in the editor.
*
* If an exact match, returns a single element. If the line is between elements,
* returns the element prior to and the element after the given line.
*
* @param {number} targetLine
*
* @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
*/
function getElementsForSourceLine(targetLine) {
const lineNumber = Math.floor(targetLine)
const lines = getCodeLineElements();
let previous = lines[0] || null;
for (const entry of lines) {
if (entry.line === lineNumber) {
return { previous: entry, next: null };
} else if (entry.line > lineNumber) {
return { previous, next: entry };
}
previous = entry;
}
return { previous };
}
/**
* Find the html elements that are at a specific pixel offset on the page.
*
* @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
*/
function getLineElementsAtPageOffset(offset) {
const lines = getCodeLineElements()
const position = offset - window.scrollY;
let lo = -1;
let hi = lines.length - 1;
while (lo + 1 < hi) {
const mid = Math.floor((lo + hi) / 2);
const bounds = lines[mid].element.getBoundingClientRect();
if (bounds.top + bounds.height >= position) {
hi = mid;
} else {
lo = mid;
}
}
const hiElement = lines[hi];
const hiBounds = hiElement.element.getBoundingClientRect();
if (hi >= 1 && hiBounds.top > position) {
const loElement = lines[lo];
return { previous: loElement, next: hiElement };
}
return { previous: hiElement };
}
/**
* Attempt to reveal the element for a source line in the editor.
*
* @param {number} line
*/
function scrollToRevealSourceLine(line) {
const { previous, next } = getElementsForSourceLine(line);
if (previous && settings.scrollPreviewWithEditor) {
let scrollTo = 0;
const rect = previous.element.getBoundingClientRect();
const previousTop = rect.top;
if (next && next.line !== previous.line) {
// Between two elements. Go to percentage offset between them.
const betweenProgress = (line - previous.line) / (next.line - previous.line);
const elementOffset = next.element.getBoundingClientRect().top - previousTop;
scrollTo = previousTop + betweenProgress * elementOffset;
} else {
scrollTo = previousTop;
}
window.scroll(0, Math.max(1, window.scrollY + scrollTo));
}
}
/**
* @param {number} offset
*/
function getEditorLineNumberForPageOffset(offset) {
const { previous, next } = getLineElementsAtPageOffset(offset);
if (previous) {
const previousBounds = previous.element.getBoundingClientRect();
const offsetFromPrevious = (offset - window.scrollY - previousBounds.top);
if (next) {
const progressBetweenElements = offsetFromPrevious / (next.element.getBoundingClientRect().top - previousBounds.top);
const line = previous.line + progressBetweenElements * (next.line - previous.line);
return clampLine(line);
} else {
const progressWithinElement = offsetFromPrevious / (previousBounds.height);
const line = previous.line + progressWithinElement;
return clampLine(line);
}
}
return null;
}
class ActiveLineMarker {
onDidChangeTextEditorSelection(line) {
const { previous } = getElementsForSourceLine(line);
this._update(previous && previous.element);
}
_update(before) {
this._unmarkActiveElement(this._current);
this._markActiveElement(before);
this._current = before;
}
_unmarkActiveElement(element) {
if (!element) {
return;
}
element.className = element.className.replace(/\bcode-active-line\b/g);
}
_markActiveElement(element) {
if (!element) {
return;
}
element.className += ' code-active-line';
}
}
var scrollDisabled = true;
const marker = new ActiveLineMarker();
const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings'));
function onLoad() {
if (settings.scrollPreviewWithEditor) {
setTimeout(() => {
const initialLine = +settings.line;
if (!isNaN(initialLine)) {
scrollDisabled = true;
scrollToRevealSourceLine(initialLine);
}
}, 0);
}
}
const onUpdateView = (() => {
const doScroll = throttle(line => {
scrollDisabled = true;
scrollToRevealSourceLine(line);
}, 50);
return (line, settings) => {
if (!isNaN(line)) {
settings.line = line;
doScroll(line);
}
};
})();
if (document.readyState === 'loading' || document.readyState === 'uninitialized') {
document.addEventListener('DOMContentLoaded', onLoad);
} else {
onLoad();
}
window.addEventListener('resize', () => {
scrollDisabled = true;
}, true);
window.addEventListener('message', event => {
if (event.data.source !== settings.source) {
return;
}
switch (event.data.type) {
case 'onDidChangeTextEditorSelection':
marker.onDidChangeTextEditorSelection(event.data.line);
break;
case 'updateView':
onUpdateView(event.data.line, settings);
break;
}
}, false);
document.addEventListener('dblclick', event => {
if (!settings.doubleClickToSwitchToEditor) {
return;
}
// Ignore clicks on links
for (let node = /** @type {HTMLElement} */(event.target); node; node = /** @type {HTMLElement} */(node.parentNode)) {
if (node.tagName === "A") {
return;
}
}
const offset = event.pageY;
const line = getEditorLineNumberForPageOffset(offset);
if (!isNaN(line)) {
postMessage('didClick', { line });
}
});
document.addEventListener('click', event => {
if (!event) {
return;
}
const baseElement = document.getElementsByTagName('base')[0];
/** @type {*} */
let node = event.target;
while (node) {
if (node.tagName && node.tagName === 'A' && node.href) {
if (node.getAttribute('href').startsWith('#')) {
break;
}
if (node.href.startsWith('file://') || node.href.startsWith('vscode-workspace-resource:')) {
const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-workspace-resource:)/i, '').split('#');
postCommand('_markdown.openDocumentLink', [{ path, fragment }]);
event.preventDefault();
event.stopPropagation();
break;
}
break;
}
node = node.parentNode;
}
}, true);
if (settings.scrollEditorWithPreview) {
window.addEventListener('scroll', throttle(() => {
if (scrollDisabled) {
scrollDisabled = false;
} else {
const line = getEditorLineNumberForPageOffset(window.scrollY);
if (!isNaN(line)) {
postMessage('revealLine', { line });
}
}
}, 50));
}
}());

File diff suppressed because one or more lines are too long

8463
extensions/markdown/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -270,7 +270,11 @@
]
},
"scripts": {
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown ./tsconfig.json"
"compile": "gulp compile-extension:markdown && npm run build-preview",
"watch": "npm run build-preview && gulp watch-extension:markdown",
"vscode:prepublish": "npm run build-ext && npm run build-preview",
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown ./tsconfig.json",
"build-preview": "webpack --mode development"
},
"dependencies": {
"highlight.js": "9.5.0",
@ -281,8 +285,14 @@
},
"devDependencies": {
"@types/highlight.js": "9.1.10",
"@types/lodash.throttle": "^4.1.3",
"@types/markdown-it": "0.0.2",
"@types/node": "7.0.43",
"vscode": "^1.1.10"
"lodash.throttle": "^4.1.1",
"ts-loader": "^4.0.1",
"typescript": "^2.7.2",
"vscode": "^1.1.10",
"webpack": "^4.1.0",
"webpack-cli": "^2.0.10"
}
}

View file

@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getElementsForSourceLine } from './scroll-sync';
export class ActiveLineMarker {
private _current: any;
onDidChangeTextEditorSelection(line: number) {
const { previous } = getElementsForSourceLine(line);
this._update(previous && previous.element);
}
_update(before: HTMLElement | undefined) {
this._unmarkActiveElement(this._current);
this._markActiveElement(before);
this._current = before;
}
_unmarkActiveElement(element: HTMLElement | undefined) {
if (!element) {
return;
}
element.className = element.className.replace(/\bcode-active-line\b/g, '');
}
_markActiveElement(element: HTMLElement | undefined) {
if (!element) {
return;
}
element.className += ' code-active-line';
}
}

View file

@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getSettings } from './settings';
import { getStrings } from './strings';
import { postCommand } from './messaging';
/**
* Shows an alert when there is a content security policy violation.
*/
export class CspAlerter {
private didShow = false;
constructor() {
document.addEventListener('securitypolicyviolation', () => {
this.showCspWarning();
});
window.addEventListener('message', (event) => {
if (event && event.data && event.data.name === 'vscode-did-block-svg') {
this.showCspWarning();
}
});
}
private showCspWarning() {
const strings = getStrings();
const settings = getSettings();
if (this.didShow || settings.disableSecurityWarnings) {
return;
}
this.didShow = true;
const notification = document.createElement('a');
notification.innerText = strings.cspAlertMessageText;
notification.setAttribute('id', 'code-csp-warning');
notification.setAttribute('title', strings.cspAlertMessageTitle);
notification.setAttribute('role', 'button');
notification.setAttribute('aria-label', strings.cspAlertMessageLabel);
notification.onclick = () => {
postCommand('markdown.showPreviewSecuritySelector', [settings.source]);
};
document.body.appendChild(notification);
}
}

View file

@ -3,6 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.htmlPreviewPart {
overflow: hidden;
export function onceDocumentLoaded(f: () => void) {
if (document.readyState === 'loading' || document.readyState === 'uninitialized') {
document.addEventListener('DOMContentLoaded', f);
} else {
f();
}
}

View file

@ -0,0 +1,117 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getSettings } from './settings';
import { postCommand, postMessage } from './messaging';
import { onceDocumentLoaded } from './events';
import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine } from './scroll-sync';
import { ActiveLineMarker } from './activeLineMarker';
import throttle = require('lodash.throttle');
var scrollDisabled = true;
const marker = new ActiveLineMarker();
const settings = getSettings();
onceDocumentLoaded(() => {
if (settings.scrollPreviewWithEditor) {
setTimeout(() => {
const initialLine = +settings.line;
if (!isNaN(initialLine)) {
scrollDisabled = true;
scrollToRevealSourceLine(initialLine);
}
}, 0);
}
});
const onUpdateView = (() => {
const doScroll = throttle((line: number) => {
scrollDisabled = true;
scrollToRevealSourceLine(line);
}, 50);
return (line: number, settings: any) => {
if (!isNaN(line)) {
settings.line = line;
doScroll(line);
}
};
})();
window.addEventListener('resize', () => {
scrollDisabled = true;
}, true);
window.addEventListener('message', event => {
if (event.data.source !== settings.source) {
return;
}
switch (event.data.type) {
case 'onDidChangeTextEditorSelection':
marker.onDidChangeTextEditorSelection(event.data.line);
break;
case 'updateView':
onUpdateView(event.data.line, settings);
break;
}
}, false);
document.addEventListener('dblclick', event => {
if (!settings.doubleClickToSwitchToEditor) {
return;
}
// Ignore clicks on links
for (let node = event.target as HTMLElement; node; node = node.parentNode as HTMLElement) {
if (node.tagName === 'A') {
return;
}
}
const offset = event.pageY;
const line = getEditorLineNumberForPageOffset(offset);
if (typeof line === 'number' && !isNaN(line)) {
postMessage('didClick', { line });
}
});
document.addEventListener('click', event => {
if (!event) {
return;
}
let node: any = event.target;
while (node) {
if (node.tagName && node.tagName === 'A' && node.href) {
if (node.getAttribute('href').startsWith('#')) {
break;
}
if (node.href.startsWith('file://') || node.href.startsWith('vscode-workspace-resource:')) {
const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-workspace-resource:)/i, '').split('#');
postCommand('_markdown.openDocumentLink', [{ path, fragment }]);
event.preventDefault();
event.stopPropagation();
break;
}
break;
}
node = node.parentNode;
}
}, true);
if (settings.scrollEditorWithPreview) {
window.addEventListener('scroll', throttle(() => {
if (scrollDisabled) {
scrollDisabled = false;
} else {
const line = getEditorLineNumberForPageOffset(window.scrollY);
if (typeof line === 'number' && !isNaN(line)) {
postMessage('revealLine', { line });
}
}
}, 50));
}

View file

@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { postCommand } from './messaging';
export class StyleLoadingMonitor {
private unloadedStyles: string[] = [];
constructor() {
const onStyleLoadError = (event: any) => {
const source = event.target.dataset.source;
this.unloadedStyles.push(source);
};
window.addEventListener('DOMContentLoaded', () => {
for (const link of document.getElementsByClassName('code-user-style') as HTMLCollectionOf<HTMLElement>) {
if (link.dataset.source) {
link.onerror = onStyleLoadError;
}
}
});
window.addEventListener('load', () => {
if (!this.unloadedStyles.length) {
return;
}
postCommand('_markdown.onPreviewStyleLoadError', [this.unloadedStyles]);
});
}
}

View file

@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getSettings } from './settings';
/**
* Post a message to the markdown extension
*/
export function postMessage(type: string, body: object) {
window.parent.postMessage({
type,
source: getSettings().source,
body
}, '*');
}
/**
* Post a command to be executed to the markdown extension
*/
export function postCommand(command: string, args: any[]) {
postMessage('command', { command, args });
}

View file

@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CspAlerter } from './csp';
import { StyleLoadingMonitor } from './loading';
// tslint:disable-next-line:no-unused-expression
new CspAlerter();
// tslint:disable-next-line:no-unused-expression
new StyleLoadingMonitor();

View file

@ -0,0 +1,127 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getSettings } from './settings';
function clamp(min: number, max: number, value: number) {
return Math.min(max, Math.max(min, value));
}
function clampLine(line: number) {
return clamp(0, getSettings().lineCount - 1, line);
}
export interface CodeLineElement {
element: HTMLElement;
line: number;
}
const getCodeLineElements = (() => {
let elements: CodeLineElement[];
return () => {
if (!elements) {
elements = Array.prototype.map.call(
document.getElementsByClassName('code-line'),
(element: any) => {
const line = +element.getAttribute('data-line');
return { element, line };
})
.filter((x: any) => !isNaN(x.line));
}
return elements;
};
})();
/**
* Find the html elements that map to a specific target line in the editor.
*
* If an exact match, returns a single element. If the line is between elements,
* returns the element prior to and the element after the given line.
*/
export function getElementsForSourceLine(targetLine: number): { previous: CodeLineElement; next?: CodeLineElement; } {
const lineNumber = Math.floor(targetLine);
const lines = getCodeLineElements();
let previous = lines[0] || null;
for (const entry of lines) {
if (entry.line === lineNumber) {
return { previous: entry, next: undefined };
}
else if (entry.line > lineNumber) {
return { previous, next: entry };
}
previous = entry;
}
return { previous };
}
/**
* Find the html elements that are at a specific pixel offset on the page.
*/
export function getLineElementsAtPageOffset(offset: number): { previous: CodeLineElement; next?: CodeLineElement; } {
const lines = getCodeLineElements();
const position = offset - window.scrollY;
let lo = -1;
let hi = lines.length - 1;
while (lo + 1 < hi) {
const mid = Math.floor((lo + hi) / 2);
const bounds = lines[mid].element.getBoundingClientRect();
if (bounds.top + bounds.height >= position) {
hi = mid;
}
else {
lo = mid;
}
}
const hiElement = lines[hi];
const hiBounds = hiElement.element.getBoundingClientRect();
if (hi >= 1 && hiBounds.top > position) {
const loElement = lines[lo];
return { previous: loElement, next: hiElement };
}
return { previous: hiElement };
}
/**
* Attempt to reveal the element for a source line in the editor.
*/
export function scrollToRevealSourceLine(line: number) {
const { previous, next } = getElementsForSourceLine(line);
if (previous && getSettings().scrollPreviewWithEditor) {
let scrollTo = 0;
const rect = previous.element.getBoundingClientRect();
const previousTop = rect.top;
if (next && next.line !== previous.line) {
// Between two elements. Go to percentage offset between them.
const betweenProgress = (line - previous.line) / (next.line - previous.line);
const elementOffset = next.element.getBoundingClientRect().top - previousTop;
scrollTo = previousTop + betweenProgress * elementOffset;
}
else {
scrollTo = previousTop;
}
window.scroll(0, Math.max(1, window.scrollY + scrollTo));
}
}
export function getEditorLineNumberForPageOffset(offset: number) {
const { previous, next } = getLineElementsAtPageOffset(offset);
if (previous) {
const previousBounds = previous.element.getBoundingClientRect();
const offsetFromPrevious = (offset - window.scrollY - previousBounds.top);
if (next) {
const progressBetweenElements = offsetFromPrevious / (next.element.getBoundingClientRect().top - previousBounds.top);
const line = previous.line + progressBetweenElements * (next.line - previous.line);
return clampLine(line);
}
else {
const progressWithinElement = offsetFromPrevious / (previousBounds.height);
const line = previous.line + progressWithinElement;
return clampLine(line);
}
}
return null;
}

View file

@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface PreviewSettings {
source: string;
line: number;
lineCount: number;
scrollPreviewWithEditor?: boolean;
scrollEditorWithPreview: boolean;
disableSecurityWarnings: boolean;
doubleClickToSwitchToEditor: boolean;
}
let cachedSettings: PreviewSettings | undefined = undefined;
export function getSettings(): PreviewSettings {
if (cachedSettings) {
return cachedSettings;
}
const element = document.getElementById('vscode-markdown-preview-data');
if (element) {
const data = element.getAttribute('data-settings');
if (data) {
cachedSettings = JSON.parse(data);
return cachedSettings!;
}
}
throw new Error('Could not load settings');
}

View file

@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function getStrings(): { [key: string]: string } {
const store = document.getElementById('vscode-markdown-preview-data');
if (store) {
const data = store.getAttribute('data-strings');
if (data) {
return JSON.parse(data);
}
}
throw new Error('Could not load strings');
}

View file

@ -0,0 +1,12 @@
{
"compilerOptions": {
"outDir": "./dist/",
"module": "commonjs",
"target": "es6",
"jsx": "react",
"sourceMap": true,
"strict": true,
"noImplicitAny": true,
"noUnusedLocals": true
}
}

View file

@ -33,11 +33,12 @@ export class OpenDocumentLinkCommand implements Command {
) { }
public execute(args: OpenDocumentLinkArgs) {
return this.tryOpen(args.path, args).catch(() => {
if (path.extname(args.path) === '') {
return this.tryOpen(args.path + '.md', args);
const p = decodeURIComponent(args.path);
return this.tryOpen(p, args).catch(() => {
if (path.extname(p) === '') {
return this.tryOpen(p + '.md', args);
}
const resource = vscode.Uri.file(args.path);
const resource = vscode.Uri.file(p);
return Promise.resolve(void 0)
.then(() => vscode.commands.executeCommand('vscode.open', resource))
.then(() => void 0);

View file

@ -83,8 +83,7 @@ export class MarkdownContentProvider {
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
${csp}
<meta id="vscode-markdown-preview-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '&quot;')}" data-strings="${JSON.stringify(previewStrings).replace(/"/g, '&quot;')}">
<script src="${this.extensionResourcePath('csp.js')}" nonce="${nonce}"></script>
<script src="${this.extensionResourcePath('loading.js')}" nonce="${nonce}"></script>
<script src="${this.extensionResourcePath('pre.js')}" nonce="${nonce}"></script>
${this.getStyles(sourceUri, nonce, config)}
<base href="${markdownDocument.uri.with({ scheme: 'vscode-workspace-resource' }).toString(true)}">
</head>
@ -165,7 +164,7 @@ export class MarkdownContentProvider {
}
private getScripts(nonce: string): string {
const scripts = [this.extensionResourcePath('main.js')].concat(this.extraScripts.map(resource => resource.toString()));
const scripts = [this.extensionResourcePath('index.js')].concat(this.extraScripts.map(resource => resource.toString()));
return scripts
.map(source => `<script async src="${source}" nonce="${nonce}" charset="UTF-8"></script>`)
.join('\n');
@ -174,14 +173,14 @@ export class MarkdownContentProvider {
private getCspForResource(resource: vscode.Uri, nonce: string): string {
switch (this.cspArbiter.getSecurityLevelForResource(resource)) {
case MarkdownPreviewSecurityLevel.AllowInsecureContent:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-workspace-resource: vscode-extension-resource: http: https: data:; media-src vscode-workspace-resource: vscode-extension-resource: http: https: data:; script-src 'nonce-${nonce}'; style-src vscode-workspace-resource: 'unsafe-inline' http: https: data: vscode-extension-resource:; font-src vscode-workspace-resource: vscode-workspace-resource: http: https: data:;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-workspace-resource: vscode-extension-resource: http: https: data:; media-src vscode-workspace-resource: vscode-extension-resource: http: https: data:; script-src 'nonce-${nonce}'; style-src vscode-workspace-resource: 'unsafe-inline' http: https: data: vscode-extension-resource:; font-src vscode-workspace-resource: vscode-extension-resource: http: https: data:;">`;
case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
return '';
case MarkdownPreviewSecurityLevel.Strict:
default:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-workspace-resource: vscode-extension-resource: https: data:; media-src vscode-workspace-resource: vscode-extension-resource: https: data:; script-src 'nonce-${nonce}'; style-src vscode-workspace-resource: 'unsafe-inline' https: data: vscode-extension-resource:; font-src vscode-workspace-resource: vscode-workspace-resource: https: data:;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-workspace-resource: vscode-extension-resource: https: data:; media-src vscode-workspace-resource: vscode-extension-resource: https: data:; script-src 'nonce-${nonce}'; style-src vscode-workspace-resource: 'unsafe-inline' https: data: vscode-extension-resource:; font-src vscode-workspace-resource: vscode-extension-resource: https: data:;">`;
}
}
}

View file

@ -150,9 +150,15 @@ export class MarkdownEngine {
}
if (fragment) {
uri = uri.with({ fragment });
uri = uri.with({
fragment: Slug.fromHeading(fragment).value
});
}
return normalizeLink(uri.with({ scheme: 'vscode-workspace-resource' }).toString(true));
} else if (!uri.scheme && !uri.path && uri.fragment) {
return normalizeLink(uri.with({
fragment: Slug.fromHeading(uri.fragment).value
}).toString(true));
}
} catch (e) {
// noop

View file

@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path = require('path');
module.exports = {
entry: {
index: './preview-src/index.ts',
pre: './preview-src/pre.ts'
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
devtool: 'inline-source-map',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'media')
}
};

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,8 @@ are treated as build tasks.
To run scripts as tasks you use the `Tasks` menu.
For more information about auto detection of Tasks pls see the [documentation](https://code.visualstudio.com/Docs/editor/tasks#_task-autodetection).
## Settings
- `npm.autoDetect` enable detecting scripts as tasks, the default is `on`.
- `npm.runSilent` run npm script with the `--silent` option, the default is `false`.

View file

@ -409,10 +409,6 @@
"debugExceptionWidget.background": "#051336",
"debugExceptionWidget.border": "#AB395B",
// Workbench: Notifications
"notification.background": "#182543",
// "notification.foreground": "",
// Workbench: Quick Open
"pickerGroup.border": "#596F99",
"pickerGroup.foreground": "#596F99",

View file

@ -37,7 +37,6 @@
"peekViewResult.background": "#362712",
"peekViewEditor.background": "#221a14",
"peekViewEditor.matchHighlightBackground": "#84613daa",
"notification.background": "#473a29",
"button.background": "#6e583b",
"inputValidation.infoBorder": "#1b60a5",
"inputValidation.infoBackground": "#2b2a42",

View file

@ -35,7 +35,6 @@
"activityBarBadge.background": "#3655b5",
"sideBar.background": "#272727",
"sideBarSectionHeader.background": "#505050",
"notification.background": "#353535",
"pickerGroup.foreground": "#b0b0b0",
"terminal.ansiWhite": "#ffffff",
"inputOption.activeBorder": "#3655b5",

View file

@ -519,7 +519,6 @@
"activityBarBadge.background": "#705697",
"titleBar.activeBackground": "#c4b7d7",
"button.background": "#705697",
"notification.background": "#442e66",
"editorGroup.dropBackground": "#C9D0D988",
"inputValidation.infoBorder": "#4ec1e5",
"inputValidation.infoBackground": "#f2fcff",

View file

@ -49,7 +49,6 @@
"list.dropBackground": "#662222",
"list.focusBackground": "#660000",
"list.highlightForeground": "#ff4444",
"notification.background": "#662222",
"pickerGroup.foreground": "#cc9999",
"pickerGroup.border": "#ff000033",
"badge.background": "#cc3333",

View file

@ -457,10 +457,6 @@
"debugExceptionWidget.background": "#00212B",
"debugExceptionWidget.border": "#AB395B",
// Workbench: Notifications
"notification.background": "#003847",
// "notification.foreground": "",
// Workbench: Quick Open
"pickerGroup.foreground": "#2AA19899",
"pickerGroup.border": "#2AA19899",

View file

@ -456,10 +456,6 @@
"debugExceptionWidget.background": "#DDD6C1",
"debugExceptionWidget.border": "#AB395B",
// Workbench: Notifications
"notification.background": "#999178",
// "notification.foreground": "",
// Workbench: Quick Open
"pickerGroup.border": "#2AA19899",
"pickerGroup.foreground": "#2AA19899",

View file

@ -552,6 +552,7 @@
"name": "tsc",
"label": "%typescript.problemMatchers.tsc.label%",
"owner": "typescript",
"source": "ts",
"applyTo": "closedDocuments",
"fileLocation": [
"relative",
@ -563,6 +564,7 @@
"name": "tsc-watch",
"label": "%typescript.problemMatchers.tscWatch.label%",
"owner": "typescript",
"source": "ts",
"applyTo": "closedDocuments",
"fileLocation": [
"relative",

View file

@ -46,7 +46,7 @@
"vscode-debugprotocol": "1.27.0",
"vscode-ripgrep": "0.7.1-patch.0.1",
"vscode-textmate": "^3.2.0",
"vscode-xterm": "3.2.0-beta7",
"vscode-xterm": "3.3.0-beta2",
"yauzl": "2.8.0"
},
"devDependencies": {

View file

@ -18,7 +18,7 @@ if %errorlevel% neq 0 exit /b %errorlevel%
call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disableExtensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR%
call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% .
if %errorlevel% neq 0 exit /b %errorlevel%
:: Integration & performance tests in AMD

View file

@ -16,7 +16,9 @@ cd $ROOT
./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
mkdir $ROOT/extensions/emmet/test-fixtures
./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started .
rm -r $ROOT/extensions/emmet/test-fixtures
# Integration tests in AMD
./scripts/test.sh --runGlob **/*.integrationTest.js "$@"

View file

@ -12,6 +12,7 @@
"noImplicitReturns": true,
"noUnusedLocals": true,
"noImplicitThis": true,
"alwaysStrict": true,
"baseUrl": ".",
"outDir": "../out",
"typeRoots": [

View file

@ -11,4 +11,4 @@ declare module 'windows-mutex' {
}
export function isActive(name: string): boolean;
}
}

View file

@ -9,6 +9,8 @@ declare module 'windows-process-tree' {
name: string,
children: ProcessTreeNode[]
}
function get(rootPid: number, callback: (tree: ProcessTreeNode) => void): void;
export = get;
}
}

View file

@ -15,11 +15,11 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Color } from 'vs/base/common/color';
export interface ICheckboxOpts extends ICheckboxStyles {
actionClassName: string;
title: string;
isChecked: boolean;
onChange: (viaKeyboard: boolean) => void;
onKeyDown?: (e: IKeyboardEvent) => void;
readonly actionClassName: string;
readonly title: string;
readonly isChecked: boolean;
readonly onChange: (viaKeyboard: boolean) => void;
readonly onKeyDown?: (e: IKeyboardEvent) => void;
}
export interface ICheckboxStyles {
@ -32,8 +32,8 @@ const defaultOpts = {
export class Checkbox extends Widget {
private _opts: ICheckboxOpts;
public domNode: HTMLElement;
private readonly _opts: ICheckboxOpts;
public readonly domNode: HTMLElement;
private _checked: boolean;

View file

@ -12,7 +12,7 @@ import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch';
import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions';
import { BaseActionItem, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { IContextViewProvider, IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { IMenuOptions } from 'vs/base/browser/ui/menu/menu';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { EventHelper, EventType } from 'vs/base/browser/dom';
@ -33,6 +33,7 @@ export class BaseDropdown extends ActionRunner {
private $boxContainer: Builder;
private $label: Builder;
private $contents: Builder;
private visible: boolean;
constructor(container: HTMLElement, options: IBaseDropdownOptions) {
super();
@ -58,7 +59,11 @@ export class BaseDropdown extends ActionRunner {
return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363)
}
this.show();
if (this.visible) {
this.hide();
} else {
this.show();
}
}).appendTo(this.$el);
let cleanupFn = labelRenderer(this.$label.getHTMLElement());
@ -87,11 +92,11 @@ export class BaseDropdown extends ActionRunner {
}
public show(): void {
// noop
this.visible = true;
}
public hide(): void {
// noop
this.visible = false;
}
protected onEvent(e: Event, activeElement: HTMLElement): void {
@ -135,10 +140,12 @@ export class Dropdown extends BaseDropdown {
}
public show(): void {
super.show();
this.element.addClass('active');
this.contextViewProvider.showContextView({
getAnchor: () => this.element.getHTMLElement(),
getAnchor: () => this.getAnchor(),
render: (container) => {
return this.renderContents(container);
@ -148,13 +155,21 @@ export class Dropdown extends BaseDropdown {
this.onEvent(e, activeElement);
},
onHide: () => {
this.element.removeClass('active');
}
onHide: () => this.onHide()
});
}
protected getAnchor(): HTMLElement | IAnchor {
return this.element.getHTMLElement();
}
protected onHide(): void {
this.element.removeClass('active');
}
public hide(): void {
super.hide();
if (this.contextViewProvider) {
this.contextViewProvider.hideContextView();
}
@ -217,6 +232,8 @@ export class DropdownMenu extends BaseDropdown {
}
public show(): void {
super.show();
this.element.addClass('active');
this._contextMenuProvider.showContextMenu({
@ -232,7 +249,7 @@ export class DropdownMenu extends BaseDropdown {
}
public hide(): void {
// noop
super.hide();
}
}

View file

@ -20,14 +20,14 @@ import { Color } from 'vs/base/common/color';
import { ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox';
export interface IFindInputOptions extends IFindInputStyles {
placeholder?: string;
width?: number;
validation?: IInputValidator;
label: string;
readonly placeholder?: string;
readonly width?: number;
readonly validation?: IInputValidator;
readonly label: string;
appendCaseSensitiveLabel?: string;
appendWholeWordsLabel?: string;
appendRegexLabel?: string;
readonly appendCaseSensitiveLabel?: string;
readonly appendWholeWordsLabel?: string;
readonly appendRegexLabel?: string;
}
export interface IFindInputStyles extends IInputBoxStyles {
@ -38,7 +38,7 @@ const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
export class FindInput extends Widget {
static OPTION_CHANGE: string = 'optionChange';
static readonly OPTION_CHANGE: string = 'optionChange';
private contextViewProvider: IContextViewProvider;
private width: number;

View file

@ -12,11 +12,11 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Color } from 'vs/base/common/color';
export interface IFindInputCheckboxOpts {
appendTitle: string;
isChecked: boolean;
onChange: (viaKeyboard: boolean) => void;
onKeyDown?: (e: IKeyboardEvent) => void;
inputActiveOptionBorder?: Color;
readonly appendTitle: string;
readonly isChecked: boolean;
readonly onChange: (viaKeyboard: boolean) => void;
readonly onKeyDown?: (e: IKeyboardEvent) => void;
readonly inputActiveOptionBorder?: Color;
}
const NLS_CASE_SENSITIVE_CHECKBOX_LABEL = nls.localize('caseDescription', "Match Case");

View file

@ -7,7 +7,7 @@
import { IDisposable } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom';
import * as objects from 'vs/base/common/objects';
import { render as renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
export interface IHighlight {
start: number;

View file

@ -329,21 +329,22 @@ export class InputBox extends Widget {
}
public validate(): boolean {
let result: IMessage = null;
let errorMsg: IMessage = null;
if (this.validation) {
result = this.validation(this.value);
errorMsg = this.validation(this.value);
if (!result) {
if (errorMsg) {
this.inputElement.setAttribute('aria-invalid', 'true');
this.showMessage(errorMsg);
}
else if (this.inputElement.hasAttribute('aria-invalid')) {
this.inputElement.removeAttribute('aria-invalid');
this.hideMessage();
} else {
this.inputElement.setAttribute('aria-invalid', 'true');
this.showMessage(result);
}
}
return !result;
return !errorMsg;
}
private stylesForType(type: MessageType): { border: Color; background: Color } {

View file

@ -339,7 +339,8 @@ class DOMFocusController<T> implements IDisposable {
.filter(e => !isInputElement(e.target as HTMLElement))
.map(e => new StandardKeyboardEvent(e));
onKeyDown.filter(e => e.keyCode === KeyCode.Tab).on(this.onTab, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.Tab && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey)
.on(this.onTab, this, this.disposables);
}
private onTab(e: StandardKeyboardEvent): void {

View file

@ -24,7 +24,7 @@ class MockOcticonLabel {
}
var mock: typeof octiconLabel = {
render: render,
renderOcticons: render,
OcticonLabel: <any>MockOcticonLabel
};
export = mock;

View file

@ -14,20 +14,20 @@ function expand(text: string): string {
});
}
export function render(label: string): string {
export function renderOcticons(label: string): string {
return expand(escape(label));
}
export class OcticonLabel {
private _container: HTMLElement;
private readonly _container: HTMLElement;
constructor(container: HTMLElement) {
this._container = container;
}
set text(text: string) {
this._container.innerHTML = render(text || '');
this._container.innerHTML = renderOcticons(text || '');
}
set title(title: string) {

View file

@ -378,9 +378,12 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
}
private renderSelectDropDown(container: HTMLElement): IDisposable {
dom.append(container, this.selectDropDownContainer);
container.appendChild(this.selectDropDownContainer);
this.layoutSelectDropDown();
return null;
return {
dispose: () => container.removeChild(this.selectDropDownContainer) // remove to take out the CSS rules we add
};
}
private layoutSelectDropDown() {

View file

@ -10,15 +10,20 @@
height: 100%;
}
.monaco-split-view2 > .split-view-view {
.monaco-split-view2 > .split-view-container {
width: 100%;
height: 100%;
}
.monaco-split-view2 > .split-view-container > .split-view-view {
overflow: hidden;
}
.monaco-split-view2.vertical > .split-view-view {
.monaco-split-view2.vertical > .split-view-container > .split-view-view {
width: 100%;
}
.monaco-split-view2.horizontal > .split-view-view {
.monaco-split-view2.horizontal > .split-view-container > .split-view-view {
height: 100%;
display: inline-block;
}

View file

@ -82,6 +82,7 @@ export class SplitView implements IDisposable {
private orientation: Orientation;
private el: HTMLElement;
private viewContainer: HTMLElement;
private size = 0;
private contentSize = 0;
private viewItems: IViewItem[] = [];
@ -105,6 +106,10 @@ export class SplitView implements IDisposable {
dom.addClass(this.el, 'monaco-split-view2');
dom.addClass(this.el, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal');
container.appendChild(this.el);
this.viewContainer = document.createElement('div');
dom.addClass(this.viewContainer, 'split-view-container');
this.el.appendChild(this.viewContainer);
}
addView(view: IView, size: number, index = this.viewItems.length): void {
@ -118,13 +123,13 @@ export class SplitView implements IDisposable {
const container = dom.$('.split-view-view');
if (index === this.viewItems.length) {
this.el.appendChild(container);
this.viewContainer.appendChild(container);
} else {
this.el.insertBefore(container, this.el.children.item(index));
this.viewContainer.insertBefore(container, this.viewContainer.children.item(index));
}
const onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size));
const containerDisposable = toDisposable(() => this.el.removeChild(container));
const containerDisposable = toDisposable(() => this.viewContainer.removeChild(container));
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const layoutContainer = this.orientation === Orientation.VERTICAL
@ -218,9 +223,9 @@ export class SplitView implements IDisposable {
this.viewItems.splice(to, 0, viewItem);
if (to + 1 < this.viewItems.length) {
this.el.insertBefore(viewItem.container, this.viewItems[to + 1].container);
this.viewContainer.insertBefore(viewItem.container, this.viewItems[to + 1].container);
} else {
this.el.appendChild(viewItem.container);
this.viewContainer.appendChild(viewItem.container);
}
this.layoutViews();

View file

@ -12,6 +12,7 @@ import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
import * as json from 'vs/base/common/json';
import * as extfs from 'vs/base/node/extfs';
import { isWindows } from 'vs/base/common/platform';
export interface IConfigurationChangeEvent<T> {
config: T;
@ -165,8 +166,18 @@ export class ConfigWatcher<T> implements IConfigWatcher<T>, IDisposable {
}
private onConfigFileChange(eventType: string, filename: string, isParentFolder: boolean): void {
if (isParentFolder && filename !== this.configName) {
return; // a change to a sibling file that is not our config file
if (isParentFolder) {
// Windows: in some cases the filename contains artifacts from the absolute path
// see https://github.com/nodejs/node/issues/19170
// As such, we have to ensure that the filename basename is used for comparison.
if (isWindows && filename !== this.configName) {
filename = basename(filename);
}
if (filename !== this.configName) {
return; // a change to a sibling file that is not our config file
}
}
if (this.timeoutHandle) {

View file

@ -78,7 +78,7 @@ suite('Splitview', () => {
test('empty splitview has empty DOM', () => {
const splitview = new SplitView(container);
assert.equal(container.firstElementChild.childElementCount, 0, 'split view should be empty');
assert.equal(container.firstElementChild.firstElementChild.childElementCount, 0, 'split view should be empty');
splitview.dispose();
});
@ -92,7 +92,7 @@ suite('Splitview', () => {
splitview.addView(view2, 20);
splitview.addView(view3, 20);
let viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-view');
let viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view');
assert.equal(viewQuery.length, 3, 'split view should have 3 views');
let sashQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-sash');
@ -100,7 +100,7 @@ suite('Splitview', () => {
splitview.removeView(2);
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-view');
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view');
assert.equal(viewQuery.length, 2, 'split view should have 2 views');
sashQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-sash');
@ -108,7 +108,7 @@ suite('Splitview', () => {
splitview.removeView(0);
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-view');
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view');
assert.equal(viewQuery.length, 1, 'split view should have 1 view');
sashQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-sash');
@ -116,7 +116,7 @@ suite('Splitview', () => {
splitview.removeView(0);
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-view');
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view');
assert.equal(viewQuery.length, 0, 'split view should have no views');
sashQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-sash');

View file

@ -1055,7 +1055,10 @@ export class CodeMenu {
return [new MenuItem({
label: nls.localize('miCheckForUpdates', "Check for Updates..."), click: () => setTimeout(() => {
this.reportMenuActionTelemetry('CheckForUpdate');
this.updateService.checkForUpdates(true);
const focusedWindow = this.windowsMainService.getFocusedWindow();
const context = focusedWindow ? { windowId: focusedWindow.id } : null;
this.updateService.checkForUpdates(context);
}, 0)
})];

View file

@ -5,8 +5,6 @@
'use strict';
import * as path from 'path';
import * as fs from 'fs';
import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
import { OpenContext } from 'vs/platform/windows/common/windows';
@ -35,22 +33,9 @@ export interface IBestWindowOrFolderOptions<W extends ISimpleWindow> {
export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, filePath, userHome, codeSettingsFolder, workspaceResolver }: IBestWindowOrFolderOptions<W>): W | string {
if (!newWindow && filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) {
const windowOnFilePath = findWindowOnFilePath(windows, filePath, workspaceResolver);
// 1) window wins if it has a workspace opened
if (windowOnFilePath && !!windowOnFilePath.openedWorkspace) {
if (windowOnFilePath) {
return windowOnFilePath;
}
// 2) window wins if it has a folder opened that is more specific than settings folder
const folderWithCodeSettings = !reuseWindow && findFolderWithCodeSettings(filePath, userHome, codeSettingsFolder);
if (windowOnFilePath && !(folderWithCodeSettings && folderWithCodeSettings.length > windowOnFilePath.openedFolderPath.length)) {
return windowOnFilePath;
}
// 3) finally return path to folder with settings
if (folderWithCodeSettings) {
return folderWithCodeSettings;
}
}
return !newWindow ? getLastActiveWindow(windows) : null;
@ -77,40 +62,6 @@ function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], filePath: s
return null;
}
function findFolderWithCodeSettings(filePath: string, userHome?: string, codeSettingsFolder?: string): string {
let folder = path.dirname(paths.normalize(filePath, true));
let homeFolder = userHome && paths.normalize(userHome, true);
if (!platform.isLinux) {
homeFolder = homeFolder && homeFolder.toLowerCase();
}
let previous = null;
while (folder !== previous) {
if (hasCodeSettings(folder, homeFolder, codeSettingsFolder)) {
return folder;
}
previous = folder;
folder = path.dirname(folder);
}
return null;
}
function hasCodeSettings(folder: string, normalizedUserHome?: string, codeSettingsFolder = '.vscode') {
try {
if ((platform.isLinux ? folder : folder.toLowerCase()) === normalizedUserHome) {
return fs.statSync(path.join(folder, codeSettingsFolder, 'settings.json')).isFile(); // ~/.vscode/extensions is used for extensions
}
return fs.statSync(path.join(folder, codeSettingsFolder)).isDirectory();
} catch (err) {
// assume impossible to access
}
return false;
}
export function getLastActiveWindow<W extends ISimpleWindow>(windows: W[]): W {
const lastFocusedDate = Math.max.apply(Math, windows.map(window => window.lastFocusTime));

View file

@ -48,25 +48,22 @@ suite('WindowsFinder', () => {
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
newWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later.
newWindow: true
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
reuseWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later.
reuseWindow: true
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
context: OpenContext.API
})), null);
});
test('New window with folder when no windows exist', () => {
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder'));
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt')
})), path.join(fixturesFolder, 'vscode_folder'));
})), null);
});
test('New window without folder when windows exist', () => {
@ -106,19 +103,11 @@ suite('WindowsFinder', () => {
windows,
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
})), vscodeFolderWindow);
});
test('Existing window wins over vscode folder if more specific', () => {
const window = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder') };
assert.equal(findBestWindowOrFolderForFile(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), window);
// check
assert.equal(findBestWindowOrFolderForFile(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder2', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder'));
});
test('More specific existing window wins', () => {
@ -130,37 +119,6 @@ suite('WindowsFinder', () => {
})), nestedFolderWindow);
});
test('VSCode folder wins over existing window if more specific', () => {
const window = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder') };
assert.equal(findBestWindowOrFolderForFile(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder'));
// check
assert.equal(findBestWindowOrFolderForFile(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), window);
});
test('More specific VSCode folder wins', () => {
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder'));
});
test('VSCode folder in home folder needs settings.json', () => {
// Because ~/.vscode/extensions is used for extensions, ~/.vscode is not enough as a hint.
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
userHome: path.join(fixturesFolder, 'vscode_folder')
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_home_folder', 'file.txt'),
userHome: path.join(fixturesFolder, 'vscode_home_folder')
})), path.join(fixturesFolder, 'vscode_home_folder'));
});
test('Workspace folder wins', () => {
const window = { lastFocusTime: 1, openedWorkspace: testWorkspace };
assert.equal(findBestWindowOrFolderForFile(options({

View file

@ -8,7 +8,7 @@
user-select: none;
}
.monaco-shell .colorpicker-hover[tabindex="0"]:focus {
.monaco-editor .colorpicker-hover:focus {
outline: none;
}

View file

@ -71,14 +71,6 @@
font-size: 13px;
}
.monaco-editor .find-widget.visible.noanimation {
-webkit-transition: none;
-o-transition: none;
-moz-transition: none;
-ms-transition: none;
transition: none;
}
.monaco-editor .find-widget > .find-part,
.monaco-editor .find-widget > .replace-part {
margin: 4px 0 0 17px;

View file

@ -65,10 +65,10 @@ const FIND_REPLACE_AREA_HEIGHT = 64; // The height of Find Widget when Replace I
export class FindWidgetViewZone implements IViewZone {
public afterLineNumber: number;
public readonly afterLineNumber: number;
public heightInPx: number;
public suppressMouseDown: boolean;
public domNode: HTMLElement;
public readonly suppressMouseDown: boolean;
public readonly domNode: HTMLElement;
constructor(afterLineNumber: number) {
this.afterLineNumber = afterLineNumber;
@ -82,11 +82,11 @@ export class FindWidgetViewZone implements IViewZone {
export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider {
private static readonly ID = 'editor.contrib.findWidget';
private _codeEditor: ICodeEditor;
private readonly _codeEditor: ICodeEditor;
private _state: FindReplaceState;
private _controller: IFindController;
private _contextViewProvider: IContextViewProvider;
private _keybindingService: IKeybindingService;
private readonly _contextViewProvider: IContextViewProvider;
private readonly _keybindingService: IKeybindingService;
private _domNode: HTMLElement;
private _findInput: FindInput;
@ -382,12 +382,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
setTimeout(() => {
dom.addClass(this._domNode, 'visible');
this._domNode.setAttribute('aria-hidden', 'false');
if (!animate) {
dom.addClass(this._domNode, 'noanimation');
setTimeout(() => {
dom.removeClass(this._domNode, 'noanimation');
}, 200);
}
}, 0);
this._codeEditor.layoutOverlayWidget(this);
@ -726,8 +720,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
className: 'previous',
onTrigger: () => {
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, onUnexpectedError);
},
onKeyDown: (e) => { }
}
}));
// Next button
@ -736,8 +729,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
className: 'next',
onTrigger: () => {
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, onUnexpectedError);
},
onKeyDown: (e) => { }
}
}));
let findPart = document.createElement('div');
@ -828,8 +820,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
className: 'replace-all',
onTrigger: () => {
this._controller.replaceAll();
},
onKeyDown: (e) => { }
}
}));
let replacePart = document.createElement('div');
@ -858,8 +849,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._replaceInputBox.width = this._findInput.inputBox.width;
}
this._showViewZone();
},
onKeyDown: (e) => { }
}
}));
this._toggleReplaceBtn.toggleClass('expand', this._isReplaceVisible);
this._toggleReplaceBtn.toggleClass('collapse', !this._isReplaceVisible);
@ -911,19 +901,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
interface ISimpleCheckboxOpts {
parent: HTMLElement;
title: string;
onChange: () => void;
readonly parent: HTMLElement;
readonly title: string;
readonly onChange: () => void;
}
class SimpleCheckbox extends Widget {
private static _COUNTER = 0;
private _opts: ISimpleCheckboxOpts;
private _domNode: HTMLElement;
private _checkbox: HTMLInputElement;
private _label: HTMLLabelElement;
private readonly _opts: ISimpleCheckboxOpts;
private readonly _domNode: HTMLElement;
private readonly _checkbox: HTMLInputElement;
private readonly _label: HTMLLabelElement;
constructor(opts: ISimpleCheckboxOpts) {
super();
@ -992,16 +982,16 @@ class SimpleCheckbox extends Widget {
}
export interface ISimpleButtonOpts {
label: string;
className: string;
onTrigger: () => void;
onKeyDown: (e: IKeyboardEvent) => void;
readonly label: string;
readonly className: string;
readonly onTrigger: () => void;
readonly onKeyDown?: (e: IKeyboardEvent) => void;
}
export class SimpleButton extends Widget {
private _opts: ISimpleButtonOpts;
private _domNode: HTMLElement;
private readonly _opts: ISimpleButtonOpts;
private readonly _domNode: HTMLElement;
constructor(opts: ISimpleButtonOpts) {
super();
@ -1018,13 +1008,16 @@ export class SimpleButton extends Widget {
this._opts.onTrigger();
e.preventDefault();
});
this.onkeydown(this._domNode, (e) => {
if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) {
this._opts.onTrigger();
e.preventDefault();
return;
}
this._opts.onKeyDown(e);
if (this._opts.onKeyDown) {
this._opts.onKeyDown(e);
}
});
}

View file

@ -23,20 +23,20 @@ const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next mat
const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");
export abstract class SimpleFindWidget extends Widget {
protected _findInput: FindInput;
protected _domNode: HTMLElement;
protected _innerDomNode: HTMLElement;
protected _isVisible: boolean;
protected _focusTracker: dom.IFocusTracker;
protected _findInputFocusTracker: dom.IFocusTracker;
protected _findHistory: HistoryNavigator<string>;
protected _updateHistoryDelayer: Delayer<void>;
private _findInput: FindInput;
private _domNode: HTMLElement;
private _innerDomNode: HTMLElement;
private _isVisible: boolean;
private _focusTracker: dom.IFocusTracker;
private _findInputFocusTracker: dom.IFocusTracker;
private _findHistory: HistoryNavigator<string>;
private _updateHistoryDelayer: Delayer<void>;
constructor(
@IContextViewService private readonly _contextViewService: IContextViewService,
private animate: boolean = true
@IContextViewService private readonly _contextViewService: IContextViewService
) {
super();
this._findInput = this._register(new FindInput(null, this._contextViewService, {
label: NLS_FIND_INPUT_LABEL,
placeholder: NLS_FIND_INPUT_PLACEHOLDER,
@ -65,31 +65,28 @@ export abstract class SimpleFindWidget extends Widget {
}
}));
let prevBtn = new SimpleButton({
const prevBtn = new SimpleButton({
label: NLS_PREVIOUS_MATCH_BTN_LABEL,
className: 'previous',
onTrigger: () => {
this.find(true);
},
onKeyDown: (e) => { }
}
});
let nextBtn = new SimpleButton({
const nextBtn = new SimpleButton({
label: NLS_NEXT_MATCH_BTN_LABEL,
className: 'next',
onTrigger: () => {
this.find(false);
},
onKeyDown: (e) => { }
}
});
let closeBtn = new SimpleButton({
const closeBtn = new SimpleButton({
label: NLS_CLOSE_BTN_LABEL,
className: 'close-fw',
onTrigger: () => {
this.hide();
},
onKeyDown: (e) => { }
}
});
this._innerDomNode = document.createElement('div');
@ -136,8 +133,8 @@ export abstract class SimpleFindWidget extends Widget {
return this._findInput.getValue();
}
public updateTheme(theme?: ITheme): void {
let inputStyles = {
public updateTheme(theme: ITheme): void {
const inputStyles = {
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
inputBackground: theme.getColor(inputBackground),
inputForeground: theme.getColor(inputForeground),
@ -152,6 +149,14 @@ export abstract class SimpleFindWidget extends Widget {
this._findInput.style(inputStyles);
}
dipose() {
super.dispose();
if (this._domNode && this._domNode.parentElement) {
this._domNode.parentElement.removeChild(this._domNode);
}
}
public getDomNode(): HTMLElement {
return this._domNode;
}
@ -171,11 +176,7 @@ export abstract class SimpleFindWidget extends Widget {
setTimeout(() => {
dom.addClass(this._innerDomNode, 'visible');
this._innerDomNode.setAttribute('aria-hidden', 'false');
if (!this.animate) {
dom.addClass(this._innerDomNode, 'noanimation');
}
setTimeout(() => {
dom.removeClass(this._innerDomNode, 'noanimation');
this._findInput.select();
}, 200);
}, 0);
@ -201,14 +202,14 @@ export abstract class SimpleFindWidget extends Widget {
}
public showNextFindTerm() {
let next = this._findHistory.next();
const next = this._findHistory.next();
if (next) {
this._findInput.setValue(next);
}
}
public showPreviousFindTerm() {
let previous = this._findHistory.previous();
const previous = this._findHistory.previous();
if (previous) {
this._findInput.setValue(previous);
}
@ -222,7 +223,7 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.monaco-workbench .simple-find-part { background-color: ${findWidgetBGColor} !important; }`);
}
let widgetShadowColor = theme.getColor(widgetShadow);
const widgetShadowColor = theme.getColor(widgetShadow);
if (widgetShadowColor) {
collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
}

View file

@ -5,14 +5,12 @@
'use strict';
import 'vs/css!./gotoError';
import * as nls from 'vs/nls';
import { Emitter } from 'vs/base/common/event';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri';
import * as dom from 'vs/base/browser/dom';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers';
import { Position } from 'vs/editor/common/core/position';
@ -20,16 +18,11 @@ 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 { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
import { registerColor, oneOf } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
import { } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { AccessibilitySupport } from 'vs/base/common/platform';
import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/editor/common/view/editorColorRegistry';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MarkerNavigationWidget } from './gotoErrorWidget';
class MarkerModel {
@ -169,224 +162,11 @@ class MarkerModel {
return 1 + this._markers.indexOf(marker);
}
public reveal(): void {
if (this._nextIdx === -1) {
return;
}
this.withoutWatchingEditorPosition(() => {
const pos = new Position(this._markers[this._nextIdx].startLineNumber, this._markers[this._nextIdx].startColumn);
this._editor.setPosition(pos);
this._editor.revealPositionInCenter(pos, editorCommon.ScrollType.Smooth);
});
}
public dispose(): void {
this._toUnbind = dispose(this._toUnbind);
}
}
class MessageWidget {
lines: number = 0;
longestLineLength: number = 0;
private readonly _editor: ICodeEditor;
private readonly _domNode: HTMLElement;
private readonly _scrollable: ScrollableElement;
private readonly _disposables: IDisposable[] = [];
constructor(parent: HTMLElement, editor: ICodeEditor) {
this._editor = editor;
this._domNode = document.createElement('span');
this._domNode.className = 'descriptioncontainer';
this._domNode.setAttribute('aria-live', 'assertive');
this._domNode.setAttribute('role', 'alert');
this._scrollable = new ScrollableElement(this._domNode, {
horizontal: ScrollbarVisibility.Auto,
vertical: ScrollbarVisibility.Hidden,
useShadows: false,
horizontalScrollbarSize: 3
});
dom.addClass(this._scrollable.getDomNode(), 'block');
parent.appendChild(this._scrollable.getDomNode());
this._disposables.push(this._scrollable.onScroll(e => this._domNode.style.left = `-${e.scrollLeft}px`));
this._disposables.push(this._scrollable);
}
dispose(): void {
dispose(this._disposables);
}
update({ source, message }: IMarker): void {
if (source) {
this.lines = 0;
this.longestLineLength = 0;
const indent = new Array(source.length + 3 + 1).join(' ');
const lines = message.split(/\r\n|\r|\n/g);
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
this.lines += 1;
this.longestLineLength = Math.max(line.length, this.longestLineLength);
if (i === 0) {
message = `[${source}] ${line}`;
} else {
message += `\n${indent}${line}`;
}
}
} else {
this.lines = 1;
this.longestLineLength = message.length;
}
this._domNode.innerText = message;
this._editor.applyFontInfo(this._domNode);
const width = Math.floor(this._editor.getConfiguration().fontInfo.typicalFullwidthCharacterWidth * this.longestLineLength);
this._scrollable.setScrollDimensions({ scrollWidth: width });
}
layout(height: number, width: number): void {
this._scrollable.setScrollDimensions({ width });
}
}
class MarkerNavigationWidget extends ZoneWidget {
private _parentContainer: HTMLElement;
private _container: HTMLElement;
private _title: HTMLElement;
private _message: MessageWidget;
private _callOnDispose: IDisposable[] = [];
private _severity: Severity;
private _backgroundColor: Color;
constructor(
editor: ICodeEditor,
private _model: MarkerModel,
private _themeService: IThemeService
) {
super(editor, { showArrow: true, showFrame: true, isAccessible: true });
this._severity = Severity.Warning;
this._backgroundColor = Color.white;
this._applyTheme(_themeService.getTheme());
this._callOnDispose.push(_themeService.onThemeChange(this._applyTheme.bind(this)));
this.create();
this._wireModelAndView();
}
private _applyTheme(theme: ITheme) {
this._backgroundColor = theme.getColor(editorMarkerNavigationBackground);
let colorId = editorMarkerNavigationError;
if (this._severity === Severity.Warning) {
colorId = editorMarkerNavigationWarning;
} else if (this._severity === Severity.Info) {
colorId = editorMarkerNavigationInfo;
}
let frameColor = theme.getColor(colorId);
this.style({
arrowColor: frameColor,
frameColor: frameColor
}); // style() will trigger _applyStyles
}
protected _applyStyles(): void {
if (this._parentContainer) {
this._parentContainer.style.backgroundColor = this._backgroundColor.toString();
}
super._applyStyles();
}
dispose(): void {
this._callOnDispose = dispose(this._callOnDispose);
super.dispose();
}
focus(): void {
this._parentContainer.focus();
}
protected _fillContainer(container: HTMLElement): void {
this._parentContainer = container;
dom.addClass(container, 'marker-widget');
this._parentContainer.tabIndex = 0;
this._parentContainer.setAttribute('role', 'tooltip');
this._container = document.createElement('div');
container.appendChild(this._container);
this._title = document.createElement('div');
this._title.className = 'block title';
this._container.appendChild(this._title);
this._message = new MessageWidget(this._container, this.editor);
this._disposables.push(this._message);
}
show(where: Position, heightInLines: number): void {
super.show(where, heightInLines);
if (this.editor.getConfiguration().accessibilitySupport !== AccessibilitySupport.Disabled) {
this.focus();
}
}
private _wireModelAndView(): void {
// listen to events
this._model.onCurrentMarkerChanged(this.showAtMarker, this, this._callOnDispose);
this._model.onMarkerSetChanged(this._onMarkersChanged, this, this._callOnDispose);
}
public showAtMarker(marker: IMarker): void {
if (!marker) {
return;
}
// update:
// * title
// * message
this._container.classList.remove('stale');
this._title.innerHTML = nls.localize('title.wo_source', "({0}/{1})", this._model.indexOf(marker), this._model.total);
this._message.update(marker);
this._model.withoutWatchingEditorPosition(() => {
// update frame color (only applied on 'show')
this._severity = marker.severity;
this._applyTheme(this._themeService.getTheme());
this.show(new Position(marker.startLineNumber, marker.startColumn), this.computeRequiredHeight());
});
}
private _onMarkersChanged(): void {
const marker = this._model.findMarkerAtPosition(this.position);
if (marker) {
this._container.classList.remove('stale');
this._message.update(marker);
} else {
this._container.classList.add('stale');
}
this._relayout();
}
protected _doLayout(heightInPixel: number, widthInPixel: number): void {
this._message.layout(heightInPixel, widthInPixel);
}
protected _relayout(): void {
super._relayout(this.computeRequiredHeight());
}
private computeRequiredHeight() {
return 1 + this._message.lines;
}
}
class MarkerNavigationAction extends EditorAction {
private _isNext: boolean;
@ -402,14 +182,11 @@ class MarkerNavigationAction extends EditorAction {
return;
}
let model = controller.getOrCreateModel();
if (model) {
if (this._isNext) {
model.next();
} else {
model.previous();
}
model.reveal();
const model = controller.getOrCreateModel();
if (this._isNext) {
model.next();
} else {
model.previous();
}
}
}
@ -424,9 +201,9 @@ class MarkerController implements editorCommon.IEditorContribution {
private _editor: ICodeEditor;
private _model: MarkerModel;
private _zone: MarkerNavigationWidget;
private _callOnClose: IDisposable[] = [];
private _markersNavigationVisible: IContextKey<boolean>;
private _widget: MarkerNavigationWidget;
private _widgetVisible: IContextKey<boolean>;
private _disposeOnClose: IDisposable[] = [];
constructor(
editor: ICodeEditor,
@ -435,7 +212,7 @@ class MarkerController implements editorCommon.IEditorContribution {
@IThemeService private readonly _themeService: IThemeService
) {
this._editor = editor;
this._markersNavigationVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
}
public getId(): string {
@ -447,9 +224,9 @@ class MarkerController implements editorCommon.IEditorContribution {
}
private _cleanUp(): void {
this._markersNavigationVisible.reset();
this._callOnClose = dispose(this._callOnClose);
this._zone = null;
this._widgetVisible.reset();
this._disposeOnClose = dispose(this._disposeOnClose);
this._widget = null;
this._model = null;
}
@ -461,15 +238,33 @@ class MarkerController implements editorCommon.IEditorContribution {
const markers = this._getMarkers();
this._model = new MarkerModel(this._editor, markers);
this._zone = new MarkerNavigationWidget(this._editor, this._model, this._themeService);
this._markersNavigationVisible.set(true);
this._markerService.onMarkerChanged(this._onMarkerChanged, this, this._disposeOnClose);
this._callOnClose.push(this._model);
this._callOnClose.push(this._zone);
this._widget = new MarkerNavigationWidget(this._editor, this._themeService);
this._widgetVisible.set(true);
this._disposeOnClose.push(this._model);
this._disposeOnClose.push(this._widget);
this._disposeOnClose.push(this._editor.onDidChangeModel(() => this._cleanUp()));
this._disposeOnClose.push(this._model.onCurrentMarkerChanged(marker => {
if (!marker) {
this._cleanUp();
} else {
this._model.withoutWatchingEditorPosition(() => {
this._widget.showAtMarker(marker, this._model.indexOf(marker), this._model.total);
});
}
}));
this._disposeOnClose.push(this._model.onMarkerSetChanged(() => {
const marker = this._model.findMarkerAtPosition(this._widget.position);
if (marker) {
this._widget.updateMarker(marker);
} else {
this._widget.showStale();
}
}));
this._callOnClose.push(this._editor.onDidChangeModel(() => this._cleanUp()));
this._model.onCurrentMarkerChanged(marker => !marker && this._cleanUp(), undefined, this._callOnClose);
this._markerService.onMarkerChanged(this._onMarkerChanged, this, this._callOnClose);
return this._model;
}
@ -539,14 +334,3 @@ registerEditorCommand(new MarkerCommand({
secondary: [KeyMod.Shift | KeyCode.Escape]
}
}));
// theming
let errorDefault = oneOf(editorErrorForeground, editorErrorBorder);
let warningDefault = oneOf(editorWarningForeground, editorWarningBorder);
let infoDefault = oneOf(editorInfoForeground, editorInfoBorder);
export const editorMarkerNavigationError = registerColor('editorMarkerNavigationError.background', { dark: errorDefault, light: errorDefault, hc: errorDefault }, nls.localize('editorMarkerNavigationError', 'Editor marker navigation widget error color.'));
export const editorMarkerNavigationWarning = registerColor('editorMarkerNavigationWarning.background', { dark: warningDefault, light: warningDefault, hc: warningDefault }, nls.localize('editorMarkerNavigationWarning', 'Editor marker navigation widget warning color.'));
export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationInfo.background', { dark: infoDefault, light: infoDefault, hc: infoDefault }, nls.localize('editorMarkerNavigationInfo', 'Editor marker navigation widget info color.'));
export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.'));

View file

@ -0,0 +1,226 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./gotoErrorWidget';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import Severity from 'vs/base/common/severity';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IMarker } from 'vs/platform/markers/common/markers';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
import { registerColor, oneOf } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
import { AccessibilitySupport } from 'vs/base/common/platform';
import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/editor/common/view/editorColorRegistry';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ScrollType } from 'vs/editor/common/editorCommon';
class MessageWidget {
lines: number = 0;
longestLineLength: number = 0;
private readonly _editor: ICodeEditor;
private readonly _domNode: HTMLElement;
private readonly _scrollable: ScrollableElement;
private readonly _disposables: IDisposable[] = [];
constructor(parent: HTMLElement, editor: ICodeEditor) {
this._editor = editor;
this._domNode = document.createElement('span');
this._domNode.className = 'descriptioncontainer';
this._domNode.setAttribute('aria-live', 'assertive');
this._domNode.setAttribute('role', 'alert');
this._scrollable = new ScrollableElement(this._domNode, {
horizontal: ScrollbarVisibility.Auto,
vertical: ScrollbarVisibility.Hidden,
useShadows: false,
horizontalScrollbarSize: 3
});
dom.addClass(this._scrollable.getDomNode(), 'block');
parent.appendChild(this._scrollable.getDomNode());
this._disposables.push(this._scrollable.onScroll(e => this._domNode.style.left = `-${e.scrollLeft}px`));
this._disposables.push(this._scrollable);
}
dispose(): void {
dispose(this._disposables);
}
update({ source, message }: IMarker): void {
if (source) {
this.lines = 0;
this.longestLineLength = 0;
const indent = new Array(source.length + 3 + 1).join(' ');
const lines = message.split(/\r\n|\r|\n/g);
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
this.lines += 1;
this.longestLineLength = Math.max(line.length, this.longestLineLength);
if (i === 0) {
message = `[${source}] ${line}`;
} else {
message += `\n${indent}${line}`;
}
}
} else {
this.lines = 1;
this.longestLineLength = message.length;
}
this._domNode.innerText = message;
this._editor.applyFontInfo(this._domNode);
const width = Math.floor(this._editor.getConfiguration().fontInfo.typicalFullwidthCharacterWidth * this.longestLineLength);
this._scrollable.setScrollDimensions({ scrollWidth: width });
}
layout(height: number, width: number): void {
this._scrollable.setScrollDimensions({ width });
}
}
export class MarkerNavigationWidget extends ZoneWidget {
private _parentContainer: HTMLElement;
private _container: HTMLElement;
private _title: HTMLElement;
private _message: MessageWidget;
private _callOnDispose: IDisposable[] = [];
private _severity: Severity;
private _backgroundColor: Color;
constructor(
editor: ICodeEditor,
private _themeService: IThemeService
) {
super(editor, { showArrow: true, showFrame: true, isAccessible: true });
this._severity = Severity.Warning;
this._backgroundColor = Color.white;
this._applyTheme(_themeService.getTheme());
this._callOnDispose.push(_themeService.onThemeChange(this._applyTheme.bind(this)));
this.create();
}
private _applyTheme(theme: ITheme) {
this._backgroundColor = theme.getColor(editorMarkerNavigationBackground);
let colorId = editorMarkerNavigationError;
if (this._severity === Severity.Warning) {
colorId = editorMarkerNavigationWarning;
} else if (this._severity === Severity.Info) {
colorId = editorMarkerNavigationInfo;
}
let frameColor = theme.getColor(colorId);
this.style({
arrowColor: frameColor,
frameColor: frameColor
}); // style() will trigger _applyStyles
}
protected _applyStyles(): void {
if (this._parentContainer) {
this._parentContainer.style.backgroundColor = this._backgroundColor.toString();
}
super._applyStyles();
}
dispose(): void {
this._callOnDispose = dispose(this._callOnDispose);
super.dispose();
}
focus(): void {
this._parentContainer.focus();
}
protected _fillContainer(container: HTMLElement): void {
this._parentContainer = container;
dom.addClass(container, 'marker-widget');
this._parentContainer.tabIndex = 0;
this._parentContainer.setAttribute('role', 'tooltip');
this._container = document.createElement('div');
container.appendChild(this._container);
this._title = document.createElement('div');
this._title.className = 'block title';
this._container.appendChild(this._title);
this._message = new MessageWidget(this._container, this.editor);
this._disposables.push(this._message);
}
show(where: Position, heightInLines: number): void {
throw new Error('call showAtMarker');
}
showAtMarker(marker: IMarker, markerIdx: number, markerCount: number): void {
// update:
// * title
// * message
this._container.classList.remove('stale');
this._title.innerHTML = nls.localize('title.wo_source', "({0}/{1})", markerIdx, markerCount);
this._message.update(marker);
// update frame color (only applied on 'show')
this._severity = marker.severity;
this._applyTheme(this._themeService.getTheme());
// show
let range = Range.lift(marker);
let position = range.containsPosition(this.editor.getPosition()) ? this.editor.getPosition() : range.getStartPosition();
super.show(position, this.computeRequiredHeight());
this.editor.revealPositionInCenter(position, ScrollType.Smooth);
if (this.editor.getConfiguration().accessibilitySupport !== AccessibilitySupport.Disabled) {
this.focus();
}
}
updateMarker(marker: IMarker): void {
this._container.classList.remove('stale');
this._message.update(marker);
}
showStale() {
this._container.classList.add('stale');
this._relayout();
}
protected _doLayout(heightInPixel: number, widthInPixel: number): void {
this._message.layout(heightInPixel, widthInPixel);
}
protected _relayout(): void {
super._relayout(this.computeRequiredHeight());
}
private computeRequiredHeight() {
return 1 + this._message.lines;
}
}
// theming
let errorDefault = oneOf(editorErrorForeground, editorErrorBorder);
let warningDefault = oneOf(editorWarningForeground, editorWarningBorder);
let infoDefault = oneOf(editorInfoForeground, editorInfoBorder);
export const editorMarkerNavigationError = registerColor('editorMarkerNavigationError.background', { dark: errorDefault, light: errorDefault, hc: errorDefault }, nls.localize('editorMarkerNavigationError', 'Editor marker navigation widget error color.'));
export const editorMarkerNavigationWarning = registerColor('editorMarkerNavigationWarning.background', { dark: warningDefault, light: warningDefault, hc: warningDefault }, nls.localize('editorMarkerNavigationWarning', 'Editor marker navigation widget warning color.'));
export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationInfo.background', { dark: infoDefault, light: infoDefault, hc: infoDefault }, nls.localize('editorMarkerNavigationInfo', 'Editor marker navigation widget info color.'));
export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.'));

View file

@ -361,7 +361,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
private isAuto: boolean;
private loadingTimeout: number;
private currentSuggestionDetails: TPromise<void>;
private focusedItemIndex: number;
private focusedItem: ICompletionItem;
private ignoreFocusEvents = false;
private completionModel: CompletionModel;
@ -591,28 +590,12 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
this.suggestionSupportsAutoAccept.set(!item.suggestion.noAutoAccept);
const oldFocus = this.focusedItem;
const oldFocusIndex = this.focusedItemIndex;
this.focusedItemIndex = index;
this.focusedItem = item;
if (oldFocus) {
this.ignoreFocusEvents = true;
this.list.splice(oldFocusIndex, 1, [oldFocus]);
this.ignoreFocusEvents = false;
}
this.list.reveal(index);
this.currentSuggestionDetails = item.resolve()
.then(() => {
this.ignoreFocusEvents = true;
this.list.splice(index, 1, [item]);
this.ignoreFocusEvents = false;
this.list.setFocus([index]);
this.list.reveal(index);
if (this.expandDocsSettingFromStorage()) {
this.showDetails();
} else {
@ -738,7 +721,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() });
this.focusedItem = null;
this.focusedItemIndex = null;
this.list.splice(0, this.list.length, this.completionModel.items);
if (isFrozen) {

View file

@ -39,7 +39,7 @@ import { OS } from 'vs/base/common/platform';
import { IRange } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { INotificationService, INotification, INotificationHandle, NoOpNotification } from 'vs/platform/notification/common/notification';
import { IConfirmation, IConfirmationResult, IConfirmationService } from 'vs/platform/dialogs/common/dialogs';
import { IConfirmation, IConfirmationResult, IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IPosition, Position as Pos } from 'vs/editor/common/core/position';
export class SimpleEditor implements IEditor {
@ -236,11 +236,20 @@ export class SimpleProgressService implements IProgressService {
}
}
export class SimpleConfirmationService implements IConfirmationService {
export class SimpleDialogService implements IDialogService {
public _serviceBrand: any;
public confirm(confirmation: IConfirmation): TPromise<boolean> {
public confirm(confirmation: IConfirmation): TPromise<IConfirmationResult> {
return this.doConfirm(confirmation).then(confirmed => {
return {
confirmed,
checkboxChecked: false // unsupported
} as IConfirmationResult;
});
}
private doConfirm(confirmation: IConfirmation): TPromise<boolean> {
let messageText = confirmation.message;
if (confirmation.detail) {
messageText = messageText + '\n\n' + confirmation.detail;
@ -248,15 +257,6 @@ export class SimpleConfirmationService implements IConfirmationService {
return TPromise.wrap(window.confirm(messageText));
}
public confirmWithCheckbox(confirmation: IConfirmation): TPromise<IConfirmationResult> {
return this.confirm(confirmation).then(confirmed => {
return {
confirmed,
checkboxChecked: false // unsupported
} as IConfirmationResult;
});
}
}
export class SimpleNotificationService implements INotificationService {

View file

@ -33,7 +33,7 @@ import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServ
import {
SimpleConfigurationService, SimpleResourceConfigurationService, SimpleMenuService,
SimpleProgressService, StandaloneCommandService, StandaloneKeybindingService, SimpleNotificationService,
StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleConfirmationService
StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleDialogService
} from 'vs/editor/standalone/browser/simpleServices';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IMenuService } from 'vs/platform/actions/common/actions';
@ -41,7 +41,7 @@ import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneT
import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl';
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IConfirmationService } from 'vs/platform/dialogs/common/dialogs';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
export interface IEditorContextViewService extends IContextViewService {
dispose(): void;
@ -126,7 +126,7 @@ export module StaticServices {
export const telemetryService = define(ITelemetryService, () => new StandaloneTelemetryService());
export const confirmationService = define(IConfirmationService, () => new SimpleConfirmationService());
export const dialogService = define(IDialogService, () => new SimpleDialogService());
export const notificationService = define(INotificationService, () => new SimpleNotificationService());

View file

@ -1864,8 +1864,6 @@ suite('Editor Controller - Regression tests', () => {
const viewModel = new ViewModel(0, config, model, null);
const cursor = new Cursor(config, model, viewModel);
console.log(viewModel.getLineCount());
moveTo(cursor, 1, 43, false);
moveTo(cursor, 1, 147, true);
assertCursor(cursor, new Selection(1, 43, 1, 147));

View file

@ -286,7 +286,8 @@ export class Configuration {
private _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(),
private _folderConfigurations: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>(),
private _memoryConfiguration: ConfigurationModel = new ConfigurationModel(),
private _memoryConfigurationByResource: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>()) {
private _memoryConfigurationByResource: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>(),
private _freeze: boolean = true) {
}
getValue(section: string, overrides: IConfigurationOverrides, workspace: Workspace): any {
@ -421,7 +422,10 @@ export class Configuration {
private getWorkspaceConsolidatedConfiguration(): ConfigurationModel {
if (!this._workspaceConsolidatedConfiguration) {
this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this._userConfiguration).merge(this._workspaceConfiguration).merge(this._memoryConfiguration).freeze();
this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this._userConfiguration, this._workspaceConfiguration, this._memoryConfiguration);
if (this._freeze) {
this._workspaceConfiguration = this._workspaceConfiguration.freeze();
}
}
return this._workspaceConsolidatedConfiguration;
}
@ -432,7 +436,10 @@ export class Configuration {
const workspaceConsolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();
const folderConfiguration = this._folderConfigurations.get(folder);
if (folderConfiguration) {
folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration).freeze();
folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration);
if (this._freeze) {
folderConsolidatedConfiguration = folderConsolidatedConfiguration.freeze();
}
this._foldersConsolidatedConfigurations.set(folder, folderConsolidatedConfiguration);
} else {
folderConsolidatedConfiguration = workspaceConsolidateConfiguration;
@ -493,21 +500,6 @@ export class Configuration {
}
return all;
}
public static parse(data: IConfigurationData): Configuration {
const defaultConfiguration = Configuration.parseConfigurationModel(data.defaults);
const userConfiguration = Configuration.parseConfigurationModel(data.user);
const workspaceConfiguration = Configuration.parseConfigurationModel(data.workspace);
const folders: StrictResourceMap<ConfigurationModel> = Object.keys(data.folders).reduce((result, key) => {
result.set(URI.parse(key), Configuration.parseConfigurationModel(data.folders[key]));
return result;
}, new StrictResourceMap<ConfigurationModel>());
return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders);
}
private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel {
return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze();
}
}
export class AbstractConfigurationChangeEvent {

View file

@ -22,25 +22,30 @@ export interface IConfirmation {
}
export interface IConfirmationResult {
/**
* Will be true if the dialog was confirmed with the primary button
* pressed.
*/
confirmed: boolean;
/**
* This will only be defined if the confirmation was created
* with the checkox option defined.
*/
checkboxChecked?: boolean;
}
export const IConfirmationService = createDecorator<IConfirmationService>('confirmationService');
export const IDialogService = createDecorator<IDialogService>('dialogService');
export interface IConfirmationService {
export interface IDialogService {
_serviceBrand: any;
/**
* Ask the user for confirmation with a modal dialog.
*/
confirm(confirmation: IConfirmation): TPromise<boolean>;
/**
* Ask the user for confirmation with a checkbox in a modal dialog.
*/
confirmWithCheckbox(confirmation: IConfirmation): TPromise<IConfirmationResult>;
confirm(confirmation: IConfirmation): TPromise<IConfirmationResult>;
}
export const IChoiceService = createDecorator<IChoiceService>('choiceService');

View file

@ -5,11 +5,10 @@
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { distinct, coalesce } from 'vs/base/common/arrays';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, getIdFromLocalExtensionId, areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getIdFromLocalExtensionId, areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@ -58,10 +57,11 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
return TPromise.as(result);
}
getEnablementState(identifier: IExtensionIdentifier): EnablementState {
if (this.environmentService.disableExtensions) {
getEnablementState(extension: ILocalExtension): EnablementState {
if (this.environmentService.disableExtensions && extension.type === LocalExtensionType.User) {
return EnablementState.Disabled;
}
const identifier = this._getIdentifier(extension);
if (this.hasWorkspace) {
if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) {
return EnablementState.WorkspaceEnabled;
@ -82,14 +82,14 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
}
setEnablement(arg: ILocalExtension | IExtensionIdentifier, newState: EnablementState): TPromise<boolean> {
let identifier;
let identifier: IExtensionIdentifier;
if (isIExtensionIdentifier(arg)) {
identifier = arg;
} else {
if (!this.canChangeEnablement(arg)) {
return TPromise.wrap(false);
}
identifier = { id: getGalleryExtensionIdFromLocal(arg), uuid: arg.identifier.uuid };
identifier = this._getIdentifier(arg);
}
const workspace = newState === EnablementState.WorkspaceDisabled || newState === EnablementState.WorkspaceEnabled;
@ -97,7 +97,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
return TPromise.wrapError<boolean>(new Error(localize('noWorkspace', "No workspace.")));
}
const currentState = this.getEnablementState(identifier);
const currentState = this._getEnablementState(identifier);
if (currentState === newState) {
return TPromise.as(false);
@ -123,16 +123,29 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
return TPromise.as(true);
}
isEnabled(identifier: IExtensionIdentifier): boolean {
const enablementState = this.getEnablementState(identifier);
isEnabled(extension: ILocalExtension): boolean {
const enablementState = this.getEnablementState(extension);
return enablementState === EnablementState.WorkspaceEnabled || enablementState === EnablementState.Enabled;
}
migrateToIdentifiers(installed: IExtensionIdentifier[]): void {
this._migrateDisabledExtensions(installed, StorageScope.GLOBAL);
private _getEnablementState(identifier: IExtensionIdentifier): EnablementState {
if (this.hasWorkspace) {
this._migrateDisabledExtensions(installed, StorageScope.WORKSPACE);
if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) {
return EnablementState.WorkspaceEnabled;
}
if (this._getDisabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) {
return EnablementState.WorkspaceDisabled;
}
}
if (this._getDisabledExtensions(StorageScope.GLOBAL).filter(e => areSameExtensions(e, identifier))[0]) {
return EnablementState.Disabled;
}
return EnablementState.Enabled;
}
private _getIdentifier(extension: ILocalExtension): IExtensionIdentifier {
return { id: getGalleryExtensionIdFromLocal(extension), uuid: extension.identifier.uuid };
}
private _enableExtension(identifier: IExtensionIdentifier): void {
@ -227,8 +240,8 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
return this._getExtensions(DISABLED_EXTENSIONS_STORAGE_PATH, scope);
}
private _setDisabledExtensions(disabledExtensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier, fireEvent = true): void {
this._setExtensions(DISABLED_EXTENSIONS_STORAGE_PATH, disabledExtensions, scope, extension, fireEvent);
private _setDisabledExtensions(disabledExtensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier): void {
this._setExtensions(DISABLED_EXTENSIONS_STORAGE_PATH, disabledExtensions, scope, extension);
}
private _getExtensions(storageId: string, scope: StorageScope): IExtensionIdentifier[] {
@ -239,30 +252,12 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
return value ? JSON.parse(value) : [];
}
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier, fireEvent = true): void {
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier): void {
if (extensions.length) {
this.storageService.store(storageId, JSON.stringify(extensions.map(({ id, uuid }) => (<IExtensionIdentifier>{ id, uuid }))), scope);
} else {
this.storageService.remove(storageId, scope);
}
if (fireEvent) {
this._onEnablementChanged.fire(extension);
}
}
private _migrateDisabledExtensions(installedExtensions: IExtensionIdentifier[], scope: StorageScope): void {
const oldValue = this.storageService.get('extensions/disabled', scope, '');
if (oldValue) {
const extensionIdentifiers = coalesce(distinct(oldValue.split(',')).map(id => {
id = adoptToGalleryExtensionId(id);
const matched = installedExtensions.filter(installed => areSameExtensions({ id }, { id: installed.id }))[0];
return matched ? { id: matched.id, uuid: matched.uuid } : null;
}));
if (extensionIdentifiers.length) {
this.storageService.store(DISABLED_EXTENSIONS_STORAGE_PATH, JSON.stringify(extensionIdentifiers), scope);
}
}
this.storageService.remove('extensions/disabled', scope);
}
private _onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void {

View file

@ -271,10 +271,10 @@ export interface IExtensionManagementService {
onUninstallExtension: Event<IExtensionIdentifier>;
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
install(zipPath: string): TPromise<void>;
installFromGallery(extension: IGalleryExtension): TPromise<void>;
install(zipPath: string): TPromise<ILocalExtension>;
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension>;
uninstall(extension: ILocalExtension, force?: boolean): TPromise<void>;
reinstall(extension: ILocalExtension): TPromise<void>;
reinstall(extension: ILocalExtension): TPromise<ILocalExtension>;
getInstalled(type?: LocalExtensionType): TPromise<ILocalExtension[]>;
getExtensionsReport(): TPromise<IReportedExtension[]>;
@ -308,7 +308,7 @@ export interface IExtensionEnablementService {
/**
* Returns the enablement state for the given extension
*/
getEnablementState(identifier: IExtensionIdentifier): EnablementState;
getEnablementState(extension: ILocalExtension): EnablementState;
/**
* Returns `true` if the enablement can be changed.
@ -318,7 +318,7 @@ export interface IExtensionEnablementService {
/**
* Returns `true` if the given extension identifier is enabled.
*/
isEnabled(identifier: IExtensionIdentifier): boolean;
isEnabled(extension: ILocalExtension): boolean;
/**
* Enable or disable the given extension.
@ -330,12 +330,6 @@ export interface IExtensionEnablementService {
* Throws error if enablement is requested for workspace and there is no workspace
*/
setEnablement(extension: ILocalExtension, state: EnablementState): TPromise<boolean>;
/**
* TODO: @Sandy. Use setEnablement(extension: ILocalExtension, state: EnablementState): TPromise<boolean>. Use one model for extension management and runtime
*/
setEnablement(identifier: IExtensionIdentifier, state: EnablementState): TPromise<boolean>;
migrateToIdentifiers(installed: IExtensionIdentifier[]): void;
}
export const IExtensionTipsService = createDecorator<IExtensionTipsService>('extensionTipsService');

View file

@ -15,9 +15,10 @@ export interface IExtensionManagementChannel extends IChannel {
call(command: 'event:onDidInstallExtension'): TPromise<void>;
call(command: 'event:onUninstallExtension'): TPromise<void>;
call(command: 'event:onDidUninstallExtension'): TPromise<void>;
call(command: 'install', path: string): TPromise<void>;
call(command: 'installFromGallery', extension: IGalleryExtension): TPromise<void>;
call(command: 'install', path: string): TPromise<ILocalExtension>;
call(command: 'installFromGallery', extension: IGalleryExtension): TPromise<ILocalExtension>;
call(command: 'uninstall', args: [ILocalExtension, boolean]): TPromise<void>;
call(command: 'reinstall', args: [ILocalExtension]): TPromise<ILocalExtension>;
call(command: 'getInstalled'): TPromise<ILocalExtension[]>;
call(command: 'getExtensionsReport'): TPromise<IReportedExtension[]>;
call(command: string, arg?: any): TPromise<any>;
@ -73,11 +74,11 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
private _onDidUninstallExtension = eventFromCall<DidUninstallExtensionEvent>(this.channel, 'event:onDidUninstallExtension');
get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this._onDidUninstallExtension; }
install(zipPath: string): TPromise<void> {
install(zipPath: string): TPromise<ILocalExtension> {
return this.channel.call('install', zipPath);
}
installFromGallery(extension: IGalleryExtension): TPromise<void> {
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
return this.channel.call('installFromGallery', [extension]);
}
@ -85,7 +86,7 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
return this.channel.call('uninstall', [extension, force]);
}
reinstall(extension: ILocalExtension): TPromise<void> {
reinstall(extension: ILocalExtension): TPromise<ILocalExtension> {
return this.channel.call('reinstall', [extension]);
}

View file

@ -25,7 +25,7 @@ import {
import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { localizeManifest } from '../common/extensionNls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { Limiter } from 'vs/base/common/async';
import { Limiter, always } from 'vs/base/common/async';
import Event, { Emitter } from 'vs/base/common/event';
import * as semver from 'semver';
import URI from 'vs/base/common/uri';
@ -49,8 +49,6 @@ const INSTALL_ERROR_GALLERY = 'gallery';
const INSTALL_ERROR_LOCAL = 'local';
const INSTALL_ERROR_EXTRACTING = 'extracting';
const INSTALL_ERROR_DELETING = 'deleting';
const INSTALL_ERROR_READING_EXTENSION_FROM_DISK = 'readingExtension';
const INSTALL_ERROR_SAVING_METADATA = 'savingMetadata';
const INSTALL_ERROR_UNKNOWN = 'unknown';
export class ExtensionManagementError extends Error {
@ -141,7 +139,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService));
}
install(zipPath: string): TPromise<void> {
install(zipPath: string): TPromise<ILocalExtension> {
zipPath = path.resolve(zipPath);
return validateLocalExtension(zipPath)
@ -159,7 +157,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
metadata => this.installFromZipPath(identifier, zipPath, metadata, manifest),
error => this.installFromZipPath(identifier, zipPath, null, manifest))
.then(
() => this.logService.info('Successfully installed the extension:', identifier.id),
local => { this.logService.info('Successfully installed the extension:', identifier.id); return local; },
e => {
this.logService.error('Failed to install the extension:', identifier.id, e.message);
return TPromise.wrapError(e);
@ -208,7 +206,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
});
}
private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise<void> {
private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise<ILocalExtension> {
return this.installExtension({ zipPath, id: identifier.id, metadata })
.then(local => {
if (this.galleryService.isEnabled() && local.manifest.extensionDependencies && local.manifest.extensionDependencies.length) {
@ -222,12 +220,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
return local;
})
.then(
local => this._onDidInstallExtension.fire({ identifier, zipPath, local }),
local => { this._onDidInstallExtension.fire({ identifier, zipPath, local }); return local; },
error => { this._onDidInstallExtension.fire({ identifier, zipPath, error }); return TPromise.wrapError(error); }
);
}
installFromGallery(extension: IGalleryExtension): TPromise<void> {
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
this.onInstallExtensions([extension]);
return this.collectExtensionsToInstall(extension)
.then(
@ -237,13 +235,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
return this.downloadAndInstallExtensions(extensionsToInstall)
.then(
locals => this.onDidInstallExtensions(extensionsToInstall, locals, []),
locals => this.onDidInstallExtensions(extensionsToInstall, locals, [])
.then(() => locals.filter(l => areSameExtensions({ id: getGalleryExtensionIdFromLocal(l), uuid: l.identifier.uuid }, extension.identifier)[0])),
errors => this.onDidInstallExtensions(extensionsToInstall, [], errors));
},
error => this.onDidInstallExtensions([extension], [], [error]));
}
reinstall(extension: ILocalExtension): TPromise<void> {
reinstall(extension: ILocalExtension): TPromise<ILocalExtension> {
if (!this.galleryService.isEnabled()) {
return TPromise.wrapError(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")));
}
@ -256,7 +255,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
() => this.installFromGallery(galleryExtension),
e => TPromise.wrapError(new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e))))));
}
return TPromise.wrapError(new Error(nls.localize('Not Market place extension', "Only Market place Extensions can be reinstalled")));
return TPromise.wrapError(new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled")));
});
}
@ -410,43 +409,39 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
private extractAndInstall({ zipPath, id, metadata }: InstallableExtension): TPromise<ILocalExtension> {
const extensionPath = path.join(this.extensionsPath, id);
return pfs.rimraf(extensionPath)
.then(() => {
this.logService.trace(`Started extracting the extension from ${zipPath} to ${extensionPath}`);
return extract(zipPath, extensionPath, { sourcePath: 'extension', overwrite: true })
.then(
() => {
this.logService.info(`Extracted extension to ${extensionPath}:`, id);
return this.completeInstall(id, extensionPath, metadata);
},
e => TPromise.wrapError(new ExtensionManagementError(e.message, INSTALL_ERROR_EXTRACTING)))
.then(null, e => {
this.logService.info('Deleting the extracted extension', id);
return pfs.rimraf(extensionPath).then(() => TPromise.wrapError(e), () => TPromise.wrapError(e));
});
}, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
const extractPath = path.join(this.extensionsPath, `.${id}`); // Extract to temp path
return this.extract(id, zipPath, extractPath, { sourcePath: 'extension', overwrite: true })
.then(() => this.completeInstall(id, extractPath))
.then(() => this.scanExtension(id, this.extensionsPath, LocalExtensionType.User))
.then(local => {
if (metadata) {
local.metadata = metadata;
return this.saveMetadataForLocalExtension(local);
}
return local;
});
}
private completeInstall(id: string, extensionPath: string, metadata: IGalleryMetadata): TPromise<ILocalExtension> {
return TPromise.join([readManifest(extensionPath), pfs.readdir(extensionPath)])
.then(null, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_READING_EXTENSION_FROM_DISK)))
.then(([{ manifest }, children]) => {
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0];
const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)).toString() : null;
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)).toString() : null;
const type = LocalExtensionType.User;
const identifier = { id, uuid: metadata ? metadata.id : null };
private extract(id: string, zipPath: string, extractPath: string, options: any): TPromise<void> {
this.logService.trace(`Started extracting the extension from ${zipPath} to ${extractPath}`);
return pfs.rimraf(extractPath)
.then(
() => extract(zipPath, extractPath, options)
.then(
() => this.logService.info(`Extracted extension to ${extractPath}:`, id),
e => always(pfs.rimraf(extractPath), () => null)
.then(() => TPromise.wrapError(new ExtensionManagementError(e.message, INSTALL_ERROR_EXTRACTING)))),
e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
}
const local: ILocalExtension = { type, identifier, manifest, metadata, path: extensionPath, readmeUrl, changelogUrl };
this.logService.trace(`Updating metadata of the extension:`, id);
return this.saveMetadataForLocalExtension(local)
.then(() => {
this.logService.info(`Updated metadata of the extension:`, id);
return local;
}, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_SAVING_METADATA)));
private completeInstall(id: string, extractPath: string): TPromise<void> {
return pfs.rename(extractPath, path.join(this.extensionsPath, id))
.then(
() => this.logService.info('Installation compelted.', id),
e => {
this.logService.info('Deleting the extracted extension', id);
return always(pfs.rimraf(extractPath), () => null)
.then(() => TPromise.wrapError(e));
});
}
@ -721,6 +716,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
private scanExtension(folderName: string, root: string, type: LocalExtensionType): TPromise<ILocalExtension> {
if (type === LocalExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user exension folder starting with `.`
return TPromise.as(null);
}
const extensionPath = path.join(root, folderName);
return pfs.readdir(extensionPath)
.then(children => readManifest(extensionPath)
@ -855,4 +853,4 @@ export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, ver
export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string {
return getLocalExtensionId(getGalleryExtensionId(manifest.publisher, manifest.name), manifest.version);
}
}

View file

@ -98,13 +98,13 @@ suite('ExtensionEnablementService Test', () => {
test('test state of globally disabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Disabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled));
});
test('test state of globally enabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Enabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled));
});
test('test disable an extension for workspace', () => {
@ -126,59 +126,59 @@ suite('ExtensionEnablementService Test', () => {
test('test state of workspace disabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceDisabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled));
});
test('test state of workspace and globally disabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceDisabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled));
});
test('test state of workspace enabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceEnabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceEnabled));
});
test('test state of globally disabled and workspace enabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled))
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceEnabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceEnabled));
});
test('test state of an extension when disabled for workspace from workspace enabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceDisabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled));
});
test('test state of an extension when disabled globally from workspace enabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Disabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled));
});
test('test state of an extension when disabled globally from workspace disabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Disabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled));
});
test('test state of an extension when enabled globally from workspace enabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Enabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled));
});
test('test state of an extension when enabled globally from workspace disabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled))
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Enabled));
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled));
});
test('test disable an extension for workspace and then globally', () => {
@ -311,18 +311,18 @@ suite('ExtensionEnablementService Test', () => {
test('test isEnabled return false extension is disabled globally', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
.then(() => assert.ok(!testObject.isEnabled({ id: 'pub.a' })));
.then(() => assert.ok(!testObject.isEnabled(aLocalExtension('pub.a'))));
});
test('test isEnabled return false extension is disabled in workspace', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => assert.ok(!testObject.isEnabled({ id: 'pub.a' })));
.then(() => assert.ok(!testObject.isEnabled(aLocalExtension('pub.a'))));
});
test('test isEnabled return true extension is not disabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.c'), EnablementState.Disabled))
.then(() => assert.ok(testObject.isEnabled({ id: 'pub.b' })));
.then(() => assert.ok(testObject.isEnabled(aLocalExtension('pub.b'))));
});
test('test canChangeEnablement return false for language packs', () => {

View file

@ -44,7 +44,7 @@ export interface INotification {
* close automatically when invoking a secondary action.
*
* **Note:** If your intent is to show a message with actions to the user, consider
* the `IChoiceService` and `IConfirmationService` instead which are optimized for
* the `IChoiceService` or `IDialogService` instead which are optimized for
* this usecase and much easier to use!
*/
actions?: INotificationActions;
@ -129,7 +129,7 @@ export interface INotificationService {
* can be used to control the notification afterwards.
*
* **Note:** If your intent is to show a message with actions to the user, consider
* the `IChoiceService` and `IConfirmationService` instead which are optimized for
* the `IChoiceService` or `IDialogService` instead which are optimized for
* this usecase and much easier to use!
*/
notify(notification: INotification): INotificationHandle;

View file

@ -11,7 +11,7 @@ import { IRequestOptions, IRequestContext, IRequestFunction, request } from 'vs/
import { getProxyAgent } from 'vs/base/node/proxy';
import { IRequestService, IHTTPConfiguration } from 'vs/platform/request/node/request';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from '../../log/common/log';
import { ILogService } from 'vs/platform/log/common/log';
/**
* This service exposes the `request` API, while using the global

View file

@ -50,7 +50,7 @@ export enum StateType {
export type Uninitialized = { type: StateType.Uninitialized };
export type Idle = { type: StateType.Idle };
export type CheckingForUpdates = { type: StateType.CheckingForUpdates, explicit: boolean };
export type CheckingForUpdates = { type: StateType.CheckingForUpdates, context: any };
export type AvailableForDownload = { type: StateType.AvailableForDownload, update: IUpdate };
export type Downloading = { type: StateType.Downloading, update: IUpdate };
export type Downloaded = { type: StateType.Downloaded, update: IUpdate };
@ -62,7 +62,7 @@ export type State = Uninitialized | Idle | CheckingForUpdates | AvailableForDown
export const State = {
Uninitialized: { type: StateType.Uninitialized } as Uninitialized,
Idle: { type: StateType.Idle } as Idle,
CheckingForUpdates: (explicit: boolean) => ({ type: StateType.CheckingForUpdates, explicit } as CheckingForUpdates),
CheckingForUpdates: (context: any) => ({ type: StateType.CheckingForUpdates, context } as CheckingForUpdates),
AvailableForDownload: (update: IUpdate) => ({ type: StateType.AvailableForDownload, update } as AvailableForDownload),
Downloading: (update: IUpdate) => ({ type: StateType.Downloading, update } as Downloading),
Downloaded: (update: IUpdate) => ({ type: StateType.Downloaded, update } as Downloaded),
@ -85,7 +85,7 @@ export interface IUpdateService {
readonly onStateChange: Event<State>;
readonly state: State;
checkForUpdates(explicit: boolean): TPromise<void>;
checkForUpdates(context: any): TPromise<void>;
downloadUpdate(): TPromise<void>;
applyUpdate(): TPromise<void>;
quitAndInstall(): TPromise<void>;

View file

@ -12,7 +12,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { IUpdateService, State } from './update';
export interface IUpdateChannel extends IChannel {
call(command: 'checkForUpdates', arg: boolean): TPromise<void>;
call(command: 'checkForUpdates', arg: any): TPromise<void>;
call(command: 'downloadUpdate'): TPromise<void>;
call(command: 'applyUpdate'): TPromise<void>;
call(command: 'quitAndInstall'): TPromise<void>;
@ -62,8 +62,8 @@ export class UpdateChannelClient implements IUpdateService {
}, onUnexpectedError);
}
checkForUpdates(explicit: boolean): TPromise<void> {
return this.channel.call('checkForUpdates', explicit);
checkForUpdates(context: any): TPromise<void> {
return this.channel.call('checkForUpdates', context);
}
downloadUpdate(): TPromise<void> {

View file

@ -81,7 +81,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
private scheduleCheckForUpdates(delay = 60 * 60 * 1000): TPromise<void> {
return TPromise.timeout(delay)
.then(() => this.checkForUpdates())
.then(() => this.checkForUpdates(null))
.then(update => {
if (update) {
// Update found, no need to check more
@ -93,14 +93,14 @@ export abstract class AbstractUpdateService implements IUpdateService {
});
}
checkForUpdates(explicit = false): TPromise<void> {
checkForUpdates(context: any): TPromise<void> {
this.logService.trace('update#checkForUpdates, state = ', this.state.type);
if (this.state.type !== StateType.Idle) {
return TPromise.as(null);
}
return this.throttler.queue(() => TPromise.as(this.doCheckForUpdates(explicit)));
return this.throttler.queue(() => TPromise.as(this.doCheckForUpdates(context)));
}
downloadUpdate(): TPromise<void> {
@ -158,5 +158,5 @@ export abstract class AbstractUpdateService implements IUpdateService {
}
protected abstract setUpdateFeedUrl(quality: string): boolean;
protected abstract doCheckForUpdates(explicit: boolean): void;
protected abstract doCheckForUpdates(context: any): void;
}

View file

@ -59,8 +59,8 @@ export class DarwinUpdateService extends AbstractUpdateService {
return true;
}
protected doCheckForUpdates(explicit: boolean): void {
this.setState(State.CheckingForUpdates(explicit));
protected doCheckForUpdates(context: any): void {
this.setState(State.CheckingForUpdates(context));
electron.autoUpdater.checkForUpdates();
}
@ -97,7 +97,7 @@ export class DarwinUpdateService extends AbstractUpdateService {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: this.state.explicit });
this.telemetryService.publicLog('update:notAvailable', { explicit: !!this.state.context });
this.setState(State.Idle);
}

View file

@ -40,12 +40,12 @@ export class LinuxUpdateService extends AbstractUpdateService {
return true;
}
protected doCheckForUpdates(explicit: boolean): void {
protected doCheckForUpdates(context: any): void {
if (!this.url) {
return;
}
this.setState(State.CheckingForUpdates(explicit));
this.setState(State.CheckingForUpdates(context));
this.requestService.request({ url: this.url })
.then<IUpdate>(asJson)
@ -56,7 +56,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit });
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle);
} else {
@ -71,7 +71,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit });
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle);
});
}

View file

@ -76,12 +76,12 @@ export class Win32UpdateService extends AbstractUpdateService {
return true;
}
protected doCheckForUpdates(explicit: boolean): void {
protected doCheckForUpdates(context: any): void {
if (!this.url) {
return;
}
this.setState(State.CheckingForUpdates(explicit));
this.setState(State.CheckingForUpdates(context));
this.requestService.request({ url: this.url })
.then<IUpdate>(asJson)
@ -92,7 +92,7 @@ export class Win32UpdateService extends AbstractUpdateService {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit });
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle);
return TPromise.as(null);
@ -137,7 +137,7 @@ export class Win32UpdateService extends AbstractUpdateService {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit });
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle);
});
}

View file

@ -10,7 +10,7 @@ import { IURLService } from 'vs/platform/url/common/url';
import product from 'vs/platform/node/product';
import { app } from 'electron';
import URI from 'vs/base/common/uri';
import { ILogService } from '../../log/common/log';
import { ILogService } from 'vs/platform/log/common/log';
export class URLService implements IURLService {

View file

@ -24,7 +24,7 @@ import { ICommandAction } from 'vs/platform/actions/common/actions';
import { Schemas } from 'vs/base/common/network';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { isWindows } from 'vs/base/common/platform';
import { ILogService } from '../../log/common/log';
import { ILogService } from 'vs/platform/log/common/log';
export class WindowsService implements IWindowsService, IDisposable {

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