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/{lib,tslintRules}/**/*.js',
'!build/**/*.sh', '!build/**/*.sh',
'!build/tfs/**/*.js', '!build/tfs/**/*.js',
'!**/Dockerfile' '!**/Dockerfile',
'!extensions/markdown/media/*.js'
]; ];
const copyrightFilter = [ const copyrightFilter = [

View file

@ -78,7 +78,7 @@ const vscodeResources = [
'out-build/vs/workbench/electron-browser/bootstrap/**', 'out-build/vs/workbench/electron-browser/bootstrap/**',
'out-build/vs/workbench/parts/debug/**/*.json', 'out-build/vs/workbench/parts/debug/**/*.json',
'out-build/vs/workbench/parts/execution/**/*.scpt', '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/**/markdown.css',
'out-build/vs/workbench/parts/tasks/**/*.json', 'out-build/vs/workbench/parts/tasks/**/*.json',
'out-build/vs/workbench/parts/terminal/electron-browser/terminalProcess.js', 'out-build/vs/workbench/parts/terminal/electron-browser/terminalProcess.js',

View file

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

View file

@ -24,6 +24,13 @@ interface ExpandAbbreviationInput {
filter?: string; filter?: string;
} }
interface PreviewRangesWithContent {
previewRange: vscode.Range;
originalRange: vscode.Range;
originalContent: string;
textToWrapInPreview: string[];
}
export function wrapWithAbbreviation(args: any) { export function wrapWithAbbreviation(args: any) {
if (!validate(false) || !vscode.window.activeTextEditor) { if (!validate(false) || !vscode.window.activeTextEditor) {
return; return;
@ -32,50 +39,145 @@ export function wrapWithAbbreviation(args: any) {
const editor = vscode.window.activeTextEditor; const editor = vscode.window.activeTextEditor;
let rootNode = parseDocument(editor.document, false); let rootNode = parseDocument(editor.document, false);
const syntax = getSyntaxFromArgs({ language: editor.document.languageId }); const syntax = getSyntaxFromArgs({ language: editor.document.languageId }) || '';
if (!syntax) { if (!syntax) {
return; 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(); const helper = getEmmetHelper();
return abbreviationPromise.then(inputAbbreviation => { function makeChanges(inputAbbreviation: string | undefined, inPreview: boolean, definitive: boolean): Thenable<boolean> {
if (!inputAbbreviation || !inputAbbreviation.trim() || !helper.isAbbreviationValid(syntax, inputAbbreviation)) { return false; } if (!inputAbbreviation || !inputAbbreviation.trim() || !helper.isAbbreviationValid(syntax, inputAbbreviation)) {
return inPreview ? revertPreview(editor, rangesToReplace).then(() => { return false; }) : Promise.resolve(inPreview);
}
let extractedResults = helper.extractAbbreviationFromText(inputAbbreviation); let extractedResults = helper.extractAbbreviationFromText(inputAbbreviation);
if (!extractedResults) { 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; 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[] = []; const expandAbbrList: ExpandAbbreviationInput[] = rangesToReplace.map(rangesAndContent => {
return { syntax, abbreviation, rangeToReplace: rangesAndContent.originalRange, textToWrap: rangesAndContent.textToWrapInPreview, filter };
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 });
}); });
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) { export function wrapIndividualLinesWithAbbreviation(args: any) {
if (!validate(false) || !vscode.window.activeTextEditor) { if (!validate(false) || !vscode.window.activeTextEditor) {
return; return;

View file

@ -309,7 +309,7 @@ export function sameNodes(node1: Node, node2: Node): boolean {
export function getEmmetConfiguration(syntax: string) { export function getEmmetConfiguration(syntax: string) {
const emmetConfig = vscode.workspace.getConfiguration('emmet'); const emmetConfig = vscode.workspace.getConfiguration('emmet');
const syntaxProfiles = Object.assign({}, emmetConfig['syntaxProfiles'] || {}); 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 // jsx, xml and xsl syntaxes need to have self closing tags unless otherwise configured by user
if (syntax === 'jsx' || syntax === 'xml' || syntax === 'xsl') { if (syntax === 'jsx' || syntax === 'xml' || syntax === 'xsl') {
syntaxProfiles[syntax] = syntaxProfiles[syntax] || {}; syntaxProfiles[syntax] = syntaxProfiles[syntax] || {};
@ -322,7 +322,7 @@ export function getEmmetConfiguration(syntax: string) {
} }
return { return {
preferences: emmetConfig['preferences'], preferences,
showExpandedAbbreviation: emmetConfig['showExpandedAbbreviation'], showExpandedAbbreviation: emmetConfig['showExpandedAbbreviation'],
showAbbreviationSuggestions: emmetConfig['showAbbreviationSuggestions'], showAbbreviationSuggestions: emmetConfig['showAbbreviationSuggestions'],
syntaxProfiles, 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) { export function contains<T>(arr: T[], val: T) {
return arr.indexOf(val) !== -1; 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'; '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 { export function applyEdits(document: TextDocument, edits: TextEdit[]): string {
let text = document.getText(); let text = document.getText();
let sortedEdits = edits.sort((a, b) => { let sortedEdits = mergeSort(edits, (a, b) => {
let startDiff = comparePositions(a.range.start, b.range.start); let diff = a.range.start.line - b.range.start.line;
if (startDiff === 0) { if (diff === 0) {
return comparePositions(a.range.end, b.range.end); 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 startOffset = document.offsetAt(e.range.start);
let endOffset = document.offsetAt(e.range.end); let endOffset = document.offsetAt(e.range.end);
text = text.substring(0, startOffset) + e.newText + text.substring(endOffset, text.length); if (endOffset <= lastModifiedOffset) {
}); text = text.substring(0, startOffset) + e.newText + text.substring(endOffset, text.length);
return text; } else {
} throw new Error('Ovelapping edit');
}
function comparePositions(p1: Position, p2: Position) { lastModifiedOffset = startOffset;
let diff = p2.line - p1.line;
if (diff === 0) {
return p2.character - p1.character;
} }
return diff; return text;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -26,6 +26,7 @@
"name": "lessc", "name": "lessc",
"label": "Lessc compiler", "label": "Lessc compiler",
"owner": "lessc", "owner": "lessc",
"source": "less",
"fileLocation": "absolute", "fileLocation": "absolute",
"pattern": { "pattern": {
"regexp": "(.*)\\sin\\s(.*)\\son line\\s(\\d+),\\scolumn\\s(\\d+)", "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": { "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": { "dependencies": {
"highlight.js": "9.5.0", "highlight.js": "9.5.0",
@ -281,8 +285,14 @@
}, },
"devDependencies": { "devDependencies": {
"@types/highlight.js": "9.1.10", "@types/highlight.js": "9.1.10",
"@types/lodash.throttle": "^4.1.3",
"@types/markdown-it": "0.0.2", "@types/markdown-it": "0.0.2",
"@types/node": "7.0.43", "@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. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
.htmlPreviewPart { export function onceDocumentLoaded(f: () => void) {
overflow: hidden; 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) { public execute(args: OpenDocumentLinkArgs) {
return this.tryOpen(args.path, args).catch(() => { const p = decodeURIComponent(args.path);
if (path.extname(args.path) === '') { return this.tryOpen(p, args).catch(() => {
return this.tryOpen(args.path + '.md', args); 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) return Promise.resolve(void 0)
.then(() => vscode.commands.executeCommand('vscode.open', resource)) .then(() => vscode.commands.executeCommand('vscode.open', resource))
.then(() => void 0); .then(() => void 0);

View file

@ -83,8 +83,7 @@ export class MarkdownContentProvider {
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
${csp} ${csp}
<meta id="vscode-markdown-preview-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '&quot;')}" data-strings="${JSON.stringify(previewStrings).replace(/"/g, '&quot;')}"> <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('pre.js')}" nonce="${nonce}"></script>
<script src="${this.extensionResourcePath('loading.js')}" nonce="${nonce}"></script>
${this.getStyles(sourceUri, nonce, config)} ${this.getStyles(sourceUri, nonce, config)}
<base href="${markdownDocument.uri.with({ scheme: 'vscode-workspace-resource' }).toString(true)}"> <base href="${markdownDocument.uri.with({ scheme: 'vscode-workspace-resource' }).toString(true)}">
</head> </head>
@ -165,7 +164,7 @@ export class MarkdownContentProvider {
} }
private getScripts(nonce: string): string { 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 return scripts
.map(source => `<script async src="${source}" nonce="${nonce}" charset="UTF-8"></script>`) .map(source => `<script async src="${source}" nonce="${nonce}" charset="UTF-8"></script>`)
.join('\n'); .join('\n');
@ -174,14 +173,14 @@ export class MarkdownContentProvider {
private getCspForResource(resource: vscode.Uri, nonce: string): string { private getCspForResource(resource: vscode.Uri, nonce: string): string {
switch (this.cspArbiter.getSecurityLevelForResource(resource)) { switch (this.cspArbiter.getSecurityLevelForResource(resource)) {
case MarkdownPreviewSecurityLevel.AllowInsecureContent: 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: case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
return ''; return '';
case MarkdownPreviewSecurityLevel.Strict: case MarkdownPreviewSecurityLevel.Strict:
default: 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) { if (fragment) {
uri = uri.with({ fragment }); uri = uri.with({
fragment: Slug.fromHeading(fragment).value
});
} }
return normalizeLink(uri.with({ scheme: 'vscode-workspace-resource' }).toString(true)); 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) { } catch (e) {
// noop // 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. 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 ## Settings
- `npm.autoDetect` enable detecting scripts as tasks, the default is `on`. - `npm.autoDetect` enable detecting scripts as tasks, the default is `on`.
- `npm.runSilent` run npm script with the `--silent` option, the default is `false`. - `npm.runSilent` run npm script with the `--silent` option, the default is `false`.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -46,7 +46,7 @@
"vscode-debugprotocol": "1.27.0", "vscode-debugprotocol": "1.27.0",
"vscode-ripgrep": "0.7.1-patch.0.1", "vscode-ripgrep": "0.7.1-patch.0.1",
"vscode-textmate": "^3.2.0", "vscode-textmate": "^3.2.0",
"vscode-xterm": "3.2.0-beta7", "vscode-xterm": "3.3.0-beta2",
"yauzl": "2.8.0" "yauzl": "2.8.0"
}, },
"devDependencies": { "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% 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% 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% if %errorlevel% neq 0 exit /b %errorlevel%
:: Integration & performance tests in AMD :: 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 --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-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/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 # Integration tests in AMD
./scripts/test.sh --runGlob **/*.integrationTest.js "$@" ./scripts/test.sh --runGlob **/*.integrationTest.js "$@"

View file

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

View file

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

View file

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

View file

@ -15,11 +15,11 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
export interface ICheckboxOpts extends ICheckboxStyles { export interface ICheckboxOpts extends ICheckboxStyles {
actionClassName: string; readonly actionClassName: string;
title: string; readonly title: string;
isChecked: boolean; readonly isChecked: boolean;
onChange: (viaKeyboard: boolean) => void; readonly onChange: (viaKeyboard: boolean) => void;
onKeyDown?: (e: IKeyboardEvent) => void; readonly onKeyDown?: (e: IKeyboardEvent) => void;
} }
export interface ICheckboxStyles { export interface ICheckboxStyles {
@ -32,8 +32,8 @@ const defaultOpts = {
export class Checkbox extends Widget { export class Checkbox extends Widget {
private _opts: ICheckboxOpts; private readonly _opts: ICheckboxOpts;
public domNode: HTMLElement; public readonly domNode: HTMLElement;
private _checked: boolean; 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 { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions';
import { BaseActionItem, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { BaseActionItem, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; 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 { IMenuOptions } from 'vs/base/browser/ui/menu/menu';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { EventHelper, EventType } from 'vs/base/browser/dom'; import { EventHelper, EventType } from 'vs/base/browser/dom';
@ -33,6 +33,7 @@ export class BaseDropdown extends ActionRunner {
private $boxContainer: Builder; private $boxContainer: Builder;
private $label: Builder; private $label: Builder;
private $contents: Builder; private $contents: Builder;
private visible: boolean;
constructor(container: HTMLElement, options: IBaseDropdownOptions) { constructor(container: HTMLElement, options: IBaseDropdownOptions) {
super(); 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) 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); }).appendTo(this.$el);
let cleanupFn = labelRenderer(this.$label.getHTMLElement()); let cleanupFn = labelRenderer(this.$label.getHTMLElement());
@ -87,11 +92,11 @@ export class BaseDropdown extends ActionRunner {
} }
public show(): void { public show(): void {
// noop this.visible = true;
} }
public hide(): void { public hide(): void {
// noop this.visible = false;
} }
protected onEvent(e: Event, activeElement: HTMLElement): void { protected onEvent(e: Event, activeElement: HTMLElement): void {
@ -135,10 +140,12 @@ export class Dropdown extends BaseDropdown {
} }
public show(): void { public show(): void {
super.show();
this.element.addClass('active'); this.element.addClass('active');
this.contextViewProvider.showContextView({ this.contextViewProvider.showContextView({
getAnchor: () => this.element.getHTMLElement(), getAnchor: () => this.getAnchor(),
render: (container) => { render: (container) => {
return this.renderContents(container); return this.renderContents(container);
@ -148,13 +155,21 @@ export class Dropdown extends BaseDropdown {
this.onEvent(e, activeElement); this.onEvent(e, activeElement);
}, },
onHide: () => { onHide: () => this.onHide()
this.element.removeClass('active');
}
}); });
} }
protected getAnchor(): HTMLElement | IAnchor {
return this.element.getHTMLElement();
}
protected onHide(): void {
this.element.removeClass('active');
}
public hide(): void { public hide(): void {
super.hide();
if (this.contextViewProvider) { if (this.contextViewProvider) {
this.contextViewProvider.hideContextView(); this.contextViewProvider.hideContextView();
} }
@ -217,6 +232,8 @@ export class DropdownMenu extends BaseDropdown {
} }
public show(): void { public show(): void {
super.show();
this.element.addClass('active'); this.element.addClass('active');
this._contextMenuProvider.showContextMenu({ this._contextMenuProvider.showContextMenu({
@ -232,7 +249,7 @@ export class DropdownMenu extends BaseDropdown {
} }
public hide(): void { 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'; import { ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox';
export interface IFindInputOptions extends IFindInputStyles { export interface IFindInputOptions extends IFindInputStyles {
placeholder?: string; readonly placeholder?: string;
width?: number; readonly width?: number;
validation?: IInputValidator; readonly validation?: IInputValidator;
label: string; readonly label: string;
appendCaseSensitiveLabel?: string; readonly appendCaseSensitiveLabel?: string;
appendWholeWordsLabel?: string; readonly appendWholeWordsLabel?: string;
appendRegexLabel?: string; readonly appendRegexLabel?: string;
} }
export interface IFindInputStyles extends IInputBoxStyles { export interface IFindInputStyles extends IInputBoxStyles {
@ -38,7 +38,7 @@ const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
export class FindInput extends Widget { export class FindInput extends Widget {
static OPTION_CHANGE: string = 'optionChange'; static readonly OPTION_CHANGE: string = 'optionChange';
private contextViewProvider: IContextViewProvider; private contextViewProvider: IContextViewProvider;
private width: number; private width: number;

View file

@ -12,11 +12,11 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
export interface IFindInputCheckboxOpts { export interface IFindInputCheckboxOpts {
appendTitle: string; readonly appendTitle: string;
isChecked: boolean; readonly isChecked: boolean;
onChange: (viaKeyboard: boolean) => void; readonly onChange: (viaKeyboard: boolean) => void;
onKeyDown?: (e: IKeyboardEvent) => void; readonly onKeyDown?: (e: IKeyboardEvent) => void;
inputActiveOptionBorder?: Color; readonly inputActiveOptionBorder?: Color;
} }
const NLS_CASE_SENSITIVE_CHECKBOX_LABEL = nls.localize('caseDescription', "Match Case"); 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 { IDisposable } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import * as objects from 'vs/base/common/objects'; 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 { export interface IHighlight {
start: number; start: number;

View file

@ -329,21 +329,22 @@ export class InputBox extends Widget {
} }
public validate(): boolean { public validate(): boolean {
let result: IMessage = null; let errorMsg: IMessage = null;
if (this.validation) { 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.inputElement.removeAttribute('aria-invalid');
this.hideMessage(); this.hideMessage();
} else {
this.inputElement.setAttribute('aria-invalid', 'true');
this.showMessage(result);
} }
} }
return !result; return !errorMsg;
} }
private stylesForType(type: MessageType): { border: Color; background: Color } { 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)) .filter(e => !isInputElement(e.target as HTMLElement))
.map(e => new StandardKeyboardEvent(e)); .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 { private onTab(e: StandardKeyboardEvent): void {

View file

@ -24,7 +24,7 @@ class MockOcticonLabel {
} }
var mock: typeof octiconLabel = { var mock: typeof octiconLabel = {
render: render, renderOcticons: render,
OcticonLabel: <any>MockOcticonLabel OcticonLabel: <any>MockOcticonLabel
}; };
export = mock; 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)); return expand(escape(label));
} }
export class OcticonLabel { export class OcticonLabel {
private _container: HTMLElement; private readonly _container: HTMLElement;
constructor(container: HTMLElement) { constructor(container: HTMLElement) {
this._container = container; this._container = container;
} }
set text(text: string) { set text(text: string) {
this._container.innerHTML = render(text || ''); this._container.innerHTML = renderOcticons(text || '');
} }
set title(title: string) { set title(title: string) {

View file

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

View file

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

View file

@ -82,6 +82,7 @@ export class SplitView implements IDisposable {
private orientation: Orientation; private orientation: Orientation;
private el: HTMLElement; private el: HTMLElement;
private viewContainer: HTMLElement;
private size = 0; private size = 0;
private contentSize = 0; private contentSize = 0;
private viewItems: IViewItem[] = []; private viewItems: IViewItem[] = [];
@ -105,6 +106,10 @@ export class SplitView implements IDisposable {
dom.addClass(this.el, 'monaco-split-view2'); dom.addClass(this.el, 'monaco-split-view2');
dom.addClass(this.el, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal'); dom.addClass(this.el, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal');
container.appendChild(this.el); 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 { 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'); const container = dom.$('.split-view-view');
if (index === this.viewItems.length) { if (index === this.viewItems.length) {
this.el.appendChild(container); this.viewContainer.appendChild(container);
} else { } 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 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 disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
const layoutContainer = this.orientation === Orientation.VERTICAL const layoutContainer = this.orientation === Orientation.VERTICAL
@ -218,9 +223,9 @@ export class SplitView implements IDisposable {
this.viewItems.splice(to, 0, viewItem); this.viewItems.splice(to, 0, viewItem);
if (to + 1 < this.viewItems.length) { 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 { } else {
this.el.appendChild(viewItem.container); this.viewContainer.appendChild(viewItem.container);
} }
this.layoutViews(); 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 Event, { Emitter } from 'vs/base/common/event';
import * as json from 'vs/base/common/json'; import * as json from 'vs/base/common/json';
import * as extfs from 'vs/base/node/extfs'; import * as extfs from 'vs/base/node/extfs';
import { isWindows } from 'vs/base/common/platform';
export interface IConfigurationChangeEvent<T> { export interface IConfigurationChangeEvent<T> {
config: T; config: T;
@ -165,8 +166,18 @@ export class ConfigWatcher<T> implements IConfigWatcher<T>, IDisposable {
} }
private onConfigFileChange(eventType: string, filename: string, isParentFolder: boolean): void { private onConfigFileChange(eventType: string, filename: string, isParentFolder: boolean): void {
if (isParentFolder && filename !== this.configName) { if (isParentFolder) {
return; // a change to a sibling file that is not our config file
// 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) { if (this.timeoutHandle) {

View file

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

View file

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

View file

@ -5,8 +5,6 @@
'use strict'; 'use strict';
import * as path from 'path';
import * as fs from 'fs';
import * as platform from 'vs/base/common/platform'; import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths'; import * as paths from 'vs/base/common/paths';
import { OpenContext } from 'vs/platform/windows/common/windows'; 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 { 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)) { if (!newWindow && filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) {
const windowOnFilePath = findWindowOnFilePath(windows, filePath, workspaceResolver); const windowOnFilePath = findWindowOnFilePath(windows, filePath, workspaceResolver);
if (windowOnFilePath) {
// 1) window wins if it has a workspace opened
if (windowOnFilePath && !!windowOnFilePath.openedWorkspace) {
return 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; return !newWindow ? getLastActiveWindow(windows) : null;
@ -77,40 +62,6 @@ function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], filePath: s
return null; 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 { export function getLastActiveWindow<W extends ISimpleWindow>(windows: W[]): W {
const lastFocusedDate = Math.max.apply(Math, windows.map(window => window.lastFocusTime)); const lastFocusedDate = Math.max.apply(Math, windows.map(window => window.lastFocusTime));

View file

@ -48,25 +48,22 @@ suite('WindowsFinder', () => {
})), null); })), null);
assert.equal(findBestWindowOrFolderForFile(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), 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); })), null);
assert.equal(findBestWindowOrFolderForFile(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), 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); })), null);
assert.equal(findBestWindowOrFolderForFile(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
context: OpenContext.API context: OpenContext.API
})), null); })), null);
});
test('New window with folder when no windows exist', () => {
assert.equal(findBestWindowOrFolderForFile(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder')); })), null);
assert.equal(findBestWindowOrFolderForFile(options({ assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt') 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', () => { test('New window without folder when windows exist', () => {
@ -106,19 +103,11 @@ suite('WindowsFinder', () => {
windows, windows,
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
})), vscodeFolderWindow); })), vscodeFolderWindow);
});
test('Existing window wins over vscode folder if more specific', () => {
const window = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder') }; const window = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder') };
assert.equal(findBestWindowOrFolderForFile(options({ assert.equal(findBestWindowOrFolderForFile(options({
windows: [window], windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt') filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), window); })), 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', () => { test('More specific existing window wins', () => {
@ -130,37 +119,6 @@ suite('WindowsFinder', () => {
})), nestedFolderWindow); })), 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', () => { test('Workspace folder wins', () => {
const window = { lastFocusTime: 1, openedWorkspace: testWorkspace }; const window = { lastFocusTime: 1, openedWorkspace: testWorkspace };
assert.equal(findBestWindowOrFolderForFile(options({ assert.equal(findBestWindowOrFolderForFile(options({

View file

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

View file

@ -71,14 +71,6 @@
font-size: 13px; 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 > .find-part,
.monaco-editor .find-widget > .replace-part { .monaco-editor .find-widget > .replace-part {
margin: 4px 0 0 17px; 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 { export class FindWidgetViewZone implements IViewZone {
public afterLineNumber: number; public readonly afterLineNumber: number;
public heightInPx: number; public heightInPx: number;
public suppressMouseDown: boolean; public readonly suppressMouseDown: boolean;
public domNode: HTMLElement; public readonly domNode: HTMLElement;
constructor(afterLineNumber: number) { constructor(afterLineNumber: number) {
this.afterLineNumber = afterLineNumber; this.afterLineNumber = afterLineNumber;
@ -82,11 +82,11 @@ export class FindWidgetViewZone implements IViewZone {
export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider { export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider {
private static readonly ID = 'editor.contrib.findWidget'; private static readonly ID = 'editor.contrib.findWidget';
private _codeEditor: ICodeEditor; private readonly _codeEditor: ICodeEditor;
private _state: FindReplaceState; private _state: FindReplaceState;
private _controller: IFindController; private _controller: IFindController;
private _contextViewProvider: IContextViewProvider; private readonly _contextViewProvider: IContextViewProvider;
private _keybindingService: IKeybindingService; private readonly _keybindingService: IKeybindingService;
private _domNode: HTMLElement; private _domNode: HTMLElement;
private _findInput: FindInput; private _findInput: FindInput;
@ -382,12 +382,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
setTimeout(() => { setTimeout(() => {
dom.addClass(this._domNode, 'visible'); dom.addClass(this._domNode, 'visible');
this._domNode.setAttribute('aria-hidden', 'false'); this._domNode.setAttribute('aria-hidden', 'false');
if (!animate) {
dom.addClass(this._domNode, 'noanimation');
setTimeout(() => {
dom.removeClass(this._domNode, 'noanimation');
}, 200);
}
}, 0); }, 0);
this._codeEditor.layoutOverlayWidget(this); this._codeEditor.layoutOverlayWidget(this);
@ -726,8 +720,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
className: 'previous', className: 'previous',
onTrigger: () => { onTrigger: () => {
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, onUnexpectedError); this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, onUnexpectedError);
}, }
onKeyDown: (e) => { }
})); }));
// Next button // Next button
@ -736,8 +729,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
className: 'next', className: 'next',
onTrigger: () => { onTrigger: () => {
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, onUnexpectedError); this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, onUnexpectedError);
}, }
onKeyDown: (e) => { }
})); }));
let findPart = document.createElement('div'); let findPart = document.createElement('div');
@ -828,8 +820,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
className: 'replace-all', className: 'replace-all',
onTrigger: () => { onTrigger: () => {
this._controller.replaceAll(); this._controller.replaceAll();
}, }
onKeyDown: (e) => { }
})); }));
let replacePart = document.createElement('div'); 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._replaceInputBox.width = this._findInput.inputBox.width;
} }
this._showViewZone(); this._showViewZone();
}, }
onKeyDown: (e) => { }
})); }));
this._toggleReplaceBtn.toggleClass('expand', this._isReplaceVisible); this._toggleReplaceBtn.toggleClass('expand', this._isReplaceVisible);
this._toggleReplaceBtn.toggleClass('collapse', !this._isReplaceVisible); this._toggleReplaceBtn.toggleClass('collapse', !this._isReplaceVisible);
@ -911,19 +901,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
} }
interface ISimpleCheckboxOpts { interface ISimpleCheckboxOpts {
parent: HTMLElement; readonly parent: HTMLElement;
title: string; readonly title: string;
onChange: () => void; readonly onChange: () => void;
} }
class SimpleCheckbox extends Widget { class SimpleCheckbox extends Widget {
private static _COUNTER = 0; private static _COUNTER = 0;
private _opts: ISimpleCheckboxOpts; private readonly _opts: ISimpleCheckboxOpts;
private _domNode: HTMLElement; private readonly _domNode: HTMLElement;
private _checkbox: HTMLInputElement; private readonly _checkbox: HTMLInputElement;
private _label: HTMLLabelElement; private readonly _label: HTMLLabelElement;
constructor(opts: ISimpleCheckboxOpts) { constructor(opts: ISimpleCheckboxOpts) {
super(); super();
@ -992,16 +982,16 @@ class SimpleCheckbox extends Widget {
} }
export interface ISimpleButtonOpts { export interface ISimpleButtonOpts {
label: string; readonly label: string;
className: string; readonly className: string;
onTrigger: () => void; readonly onTrigger: () => void;
onKeyDown: (e: IKeyboardEvent) => void; readonly onKeyDown?: (e: IKeyboardEvent) => void;
} }
export class SimpleButton extends Widget { export class SimpleButton extends Widget {
private _opts: ISimpleButtonOpts; private readonly _opts: ISimpleButtonOpts;
private _domNode: HTMLElement; private readonly _domNode: HTMLElement;
constructor(opts: ISimpleButtonOpts) { constructor(opts: ISimpleButtonOpts) {
super(); super();
@ -1018,13 +1008,16 @@ export class SimpleButton extends Widget {
this._opts.onTrigger(); this._opts.onTrigger();
e.preventDefault(); e.preventDefault();
}); });
this.onkeydown(this._domNode, (e) => { this.onkeydown(this._domNode, (e) => {
if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) { if (e.equals(KeyCode.Space) || e.equals(KeyCode.Enter)) {
this._opts.onTrigger(); this._opts.onTrigger();
e.preventDefault(); e.preventDefault();
return; 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"); const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");
export abstract class SimpleFindWidget extends Widget { export abstract class SimpleFindWidget extends Widget {
protected _findInput: FindInput; private _findInput: FindInput;
protected _domNode: HTMLElement; private _domNode: HTMLElement;
protected _innerDomNode: HTMLElement; private _innerDomNode: HTMLElement;
protected _isVisible: boolean; private _isVisible: boolean;
protected _focusTracker: dom.IFocusTracker; private _focusTracker: dom.IFocusTracker;
protected _findInputFocusTracker: dom.IFocusTracker; private _findInputFocusTracker: dom.IFocusTracker;
protected _findHistory: HistoryNavigator<string>; private _findHistory: HistoryNavigator<string>;
protected _updateHistoryDelayer: Delayer<void>; private _updateHistoryDelayer: Delayer<void>;
constructor( constructor(
@IContextViewService private readonly _contextViewService: IContextViewService, @IContextViewService private readonly _contextViewService: IContextViewService
private animate: boolean = true
) { ) {
super(); super();
this._findInput = this._register(new FindInput(null, this._contextViewService, { this._findInput = this._register(new FindInput(null, this._contextViewService, {
label: NLS_FIND_INPUT_LABEL, label: NLS_FIND_INPUT_LABEL,
placeholder: NLS_FIND_INPUT_PLACEHOLDER, 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, label: NLS_PREVIOUS_MATCH_BTN_LABEL,
className: 'previous', className: 'previous',
onTrigger: () => { onTrigger: () => {
this.find(true); this.find(true);
}, }
onKeyDown: (e) => { }
}); });
let nextBtn = new SimpleButton({ const nextBtn = new SimpleButton({
label: NLS_NEXT_MATCH_BTN_LABEL, label: NLS_NEXT_MATCH_BTN_LABEL,
className: 'next', className: 'next',
onTrigger: () => { onTrigger: () => {
this.find(false); this.find(false);
}, }
onKeyDown: (e) => { }
}); });
let closeBtn = new SimpleButton({ const closeBtn = new SimpleButton({
label: NLS_CLOSE_BTN_LABEL, label: NLS_CLOSE_BTN_LABEL,
className: 'close-fw', className: 'close-fw',
onTrigger: () => { onTrigger: () => {
this.hide(); this.hide();
}, }
onKeyDown: (e) => { }
}); });
this._innerDomNode = document.createElement('div'); this._innerDomNode = document.createElement('div');
@ -136,8 +133,8 @@ export abstract class SimpleFindWidget extends Widget {
return this._findInput.getValue(); return this._findInput.getValue();
} }
public updateTheme(theme?: ITheme): void { public updateTheme(theme: ITheme): void {
let inputStyles = { const inputStyles = {
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder), inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
inputBackground: theme.getColor(inputBackground), inputBackground: theme.getColor(inputBackground),
inputForeground: theme.getColor(inputForeground), inputForeground: theme.getColor(inputForeground),
@ -152,6 +149,14 @@ export abstract class SimpleFindWidget extends Widget {
this._findInput.style(inputStyles); this._findInput.style(inputStyles);
} }
dipose() {
super.dispose();
if (this._domNode && this._domNode.parentElement) {
this._domNode.parentElement.removeChild(this._domNode);
}
}
public getDomNode(): HTMLElement { public getDomNode(): HTMLElement {
return this._domNode; return this._domNode;
} }
@ -171,11 +176,7 @@ export abstract class SimpleFindWidget extends Widget {
setTimeout(() => { setTimeout(() => {
dom.addClass(this._innerDomNode, 'visible'); dom.addClass(this._innerDomNode, 'visible');
this._innerDomNode.setAttribute('aria-hidden', 'false'); this._innerDomNode.setAttribute('aria-hidden', 'false');
if (!this.animate) {
dom.addClass(this._innerDomNode, 'noanimation');
}
setTimeout(() => { setTimeout(() => {
dom.removeClass(this._innerDomNode, 'noanimation');
this._findInput.select(); this._findInput.select();
}, 200); }, 200);
}, 0); }, 0);
@ -201,14 +202,14 @@ export abstract class SimpleFindWidget extends Widget {
} }
public showNextFindTerm() { public showNextFindTerm() {
let next = this._findHistory.next(); const next = this._findHistory.next();
if (next) { if (next) {
this._findInput.setValue(next); this._findInput.setValue(next);
} }
} }
public showPreviousFindTerm() { public showPreviousFindTerm() {
let previous = this._findHistory.previous(); const previous = this._findHistory.previous();
if (previous) { if (previous) {
this._findInput.setValue(previous); this._findInput.setValue(previous);
} }
@ -222,7 +223,7 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.monaco-workbench .simple-find-part { background-color: ${findWidgetBGColor} !important; }`); collector.addRule(`.monaco-workbench .simple-find-part { background-color: ${findWidgetBGColor} !important; }`);
} }
let widgetShadowColor = theme.getColor(widgetShadow); const widgetShadowColor = theme.getColor(widgetShadow);
if (widgetShadowColor) { if (widgetShadowColor) {
collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`); collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
} }

View file

@ -5,14 +5,12 @@
'use strict'; 'use strict';
import 'vs/css!./gotoError';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { Emitter } from 'vs/base/common/event'; import { Emitter } from 'vs/base/common/event';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri'; 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 { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers'; import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers';
import { Position } from 'vs/editor/common/core/position'; 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 * as editorCommon from 'vs/editor/common/editorCommon';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import { } from 'vs/platform/theme/common/colorRegistry';
import { registerColor, oneOf } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; 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 { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MarkerNavigationWidget } from './gotoErrorWidget';
class MarkerModel { class MarkerModel {
@ -169,224 +162,11 @@ class MarkerModel {
return 1 + this._markers.indexOf(marker); 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 { public dispose(): void {
this._toUnbind = dispose(this._toUnbind); 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 { class MarkerNavigationAction extends EditorAction {
private _isNext: boolean; private _isNext: boolean;
@ -402,14 +182,11 @@ class MarkerNavigationAction extends EditorAction {
return; return;
} }
let model = controller.getOrCreateModel(); const model = controller.getOrCreateModel();
if (model) { if (this._isNext) {
if (this._isNext) { model.next();
model.next(); } else {
} else { model.previous();
model.previous();
}
model.reveal();
} }
} }
} }
@ -424,9 +201,9 @@ class MarkerController implements editorCommon.IEditorContribution {
private _editor: ICodeEditor; private _editor: ICodeEditor;
private _model: MarkerModel; private _model: MarkerModel;
private _zone: MarkerNavigationWidget; private _widget: MarkerNavigationWidget;
private _callOnClose: IDisposable[] = []; private _widgetVisible: IContextKey<boolean>;
private _markersNavigationVisible: IContextKey<boolean>; private _disposeOnClose: IDisposable[] = [];
constructor( constructor(
editor: ICodeEditor, editor: ICodeEditor,
@ -435,7 +212,7 @@ class MarkerController implements editorCommon.IEditorContribution {
@IThemeService private readonly _themeService: IThemeService @IThemeService private readonly _themeService: IThemeService
) { ) {
this._editor = editor; this._editor = editor;
this._markersNavigationVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService); this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
} }
public getId(): string { public getId(): string {
@ -447,9 +224,9 @@ class MarkerController implements editorCommon.IEditorContribution {
} }
private _cleanUp(): void { private _cleanUp(): void {
this._markersNavigationVisible.reset(); this._widgetVisible.reset();
this._callOnClose = dispose(this._callOnClose); this._disposeOnClose = dispose(this._disposeOnClose);
this._zone = null; this._widget = null;
this._model = null; this._model = null;
} }
@ -461,15 +238,33 @@ class MarkerController implements editorCommon.IEditorContribution {
const markers = this._getMarkers(); const markers = this._getMarkers();
this._model = new MarkerModel(this._editor, markers); this._model = new MarkerModel(this._editor, markers);
this._zone = new MarkerNavigationWidget(this._editor, this._model, this._themeService); this._markerService.onMarkerChanged(this._onMarkerChanged, this, this._disposeOnClose);
this._markersNavigationVisible.set(true);
this._callOnClose.push(this._model); this._widget = new MarkerNavigationWidget(this._editor, this._themeService);
this._callOnClose.push(this._zone); 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; return this._model;
} }
@ -539,14 +334,3 @@ registerEditorCommand(new MarkerCommand({
secondary: [KeyMod.Shift | KeyCode.Escape] 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 isAuto: boolean;
private loadingTimeout: number; private loadingTimeout: number;
private currentSuggestionDetails: TPromise<void>; private currentSuggestionDetails: TPromise<void>;
private focusedItemIndex: number;
private focusedItem: ICompletionItem; private focusedItem: ICompletionItem;
private ignoreFocusEvents = false; private ignoreFocusEvents = false;
private completionModel: CompletionModel; private completionModel: CompletionModel;
@ -591,28 +590,12 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
this.suggestionSupportsAutoAccept.set(!item.suggestion.noAutoAccept); this.suggestionSupportsAutoAccept.set(!item.suggestion.noAutoAccept);
const oldFocus = this.focusedItem;
const oldFocusIndex = this.focusedItemIndex;
this.focusedItemIndex = index;
this.focusedItem = item; this.focusedItem = item;
if (oldFocus) {
this.ignoreFocusEvents = true;
this.list.splice(oldFocusIndex, 1, [oldFocus]);
this.ignoreFocusEvents = false;
}
this.list.reveal(index); this.list.reveal(index);
this.currentSuggestionDetails = item.resolve() this.currentSuggestionDetails = item.resolve()
.then(() => { .then(() => {
this.ignoreFocusEvents = true;
this.list.splice(index, 1, [item]);
this.ignoreFocusEvents = false;
this.list.setFocus([index]);
this.list.reveal(index);
if (this.expandDocsSettingFromStorage()) { if (this.expandDocsSettingFromStorage()) {
this.showDetails(); this.showDetails();
} else { } else {
@ -738,7 +721,6 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() }); this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() });
this.focusedItem = null; this.focusedItem = null;
this.focusedItemIndex = null;
this.list.splice(0, this.list.length, this.completionModel.items); this.list.splice(0, this.list.length, this.completionModel.items);
if (isFrozen) { if (isFrozen) {

View file

@ -39,7 +39,7 @@ import { OS } from 'vs/base/common/platform';
import { IRange } from 'vs/editor/common/core/range'; import { IRange } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model'; import { ITextModel } from 'vs/editor/common/model';
import { INotificationService, INotification, INotificationHandle, NoOpNotification } from 'vs/platform/notification/common/notification'; 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'; import { IPosition, Position as Pos } from 'vs/editor/common/core/position';
export class SimpleEditor implements IEditor { 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 _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; let messageText = confirmation.message;
if (confirmation.detail) { if (confirmation.detail) {
messageText = messageText + '\n\n' + confirmation.detail; messageText = messageText + '\n\n' + confirmation.detail;
@ -248,15 +257,6 @@ export class SimpleConfirmationService implements IConfirmationService {
return TPromise.wrap(window.confirm(messageText)); 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 { export class SimpleNotificationService implements INotificationService {

View file

@ -33,7 +33,7 @@ import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServ
import { import {
SimpleConfigurationService, SimpleResourceConfigurationService, SimpleMenuService, SimpleConfigurationService, SimpleResourceConfigurationService, SimpleMenuService,
SimpleProgressService, StandaloneCommandService, StandaloneKeybindingService, SimpleNotificationService, SimpleProgressService, StandaloneCommandService, StandaloneKeybindingService, SimpleNotificationService,
StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleConfirmationService StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleDialogService
} from 'vs/editor/standalone/browser/simpleServices'; } from 'vs/editor/standalone/browser/simpleServices';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IMenuService } from 'vs/platform/actions/common/actions'; 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 { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl';
import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification'; 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 { export interface IEditorContextViewService extends IContextViewService {
dispose(): void; dispose(): void;
@ -126,7 +126,7 @@ export module StaticServices {
export const telemetryService = define(ITelemetryService, () => new StandaloneTelemetryService()); 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()); 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 viewModel = new ViewModel(0, config, model, null);
const cursor = new Cursor(config, model, viewModel); const cursor = new Cursor(config, model, viewModel);
console.log(viewModel.getLineCount());
moveTo(cursor, 1, 43, false); moveTo(cursor, 1, 43, false);
moveTo(cursor, 1, 147, true); moveTo(cursor, 1, 147, true);
assertCursor(cursor, new Selection(1, 43, 1, 147)); assertCursor(cursor, new Selection(1, 43, 1, 147));

View file

@ -286,7 +286,8 @@ export class Configuration {
private _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(), private _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(),
private _folderConfigurations: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>(), private _folderConfigurations: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>(),
private _memoryConfiguration: ConfigurationModel = new 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 { getValue(section: string, overrides: IConfigurationOverrides, workspace: Workspace): any {
@ -421,7 +422,10 @@ export class Configuration {
private getWorkspaceConsolidatedConfiguration(): ConfigurationModel { private getWorkspaceConsolidatedConfiguration(): ConfigurationModel {
if (!this._workspaceConsolidatedConfiguration) { 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; return this._workspaceConsolidatedConfiguration;
} }
@ -432,7 +436,10 @@ export class Configuration {
const workspaceConsolidateConfiguration = this.getWorkspaceConsolidatedConfiguration(); const workspaceConsolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();
const folderConfiguration = this._folderConfigurations.get(folder); const folderConfiguration = this._folderConfigurations.get(folder);
if (folderConfiguration) { if (folderConfiguration) {
folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration).freeze(); folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration);
if (this._freeze) {
folderConsolidatedConfiguration = folderConsolidatedConfiguration.freeze();
}
this._foldersConsolidatedConfigurations.set(folder, folderConsolidatedConfiguration); this._foldersConsolidatedConfigurations.set(folder, folderConsolidatedConfiguration);
} else { } else {
folderConsolidatedConfiguration = workspaceConsolidateConfiguration; folderConsolidatedConfiguration = workspaceConsolidateConfiguration;
@ -493,21 +500,6 @@ export class Configuration {
} }
return all; 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 { export class AbstractConfigurationChangeEvent {

View file

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

View file

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

View file

@ -271,10 +271,10 @@ export interface IExtensionManagementService {
onUninstallExtension: Event<IExtensionIdentifier>; onUninstallExtension: Event<IExtensionIdentifier>;
onDidUninstallExtension: Event<DidUninstallExtensionEvent>; onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
install(zipPath: string): TPromise<void>; install(zipPath: string): TPromise<ILocalExtension>;
installFromGallery(extension: IGalleryExtension): TPromise<void>; installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension>;
uninstall(extension: ILocalExtension, force?: boolean): TPromise<void>; uninstall(extension: ILocalExtension, force?: boolean): TPromise<void>;
reinstall(extension: ILocalExtension): TPromise<void>; reinstall(extension: ILocalExtension): TPromise<ILocalExtension>;
getInstalled(type?: LocalExtensionType): TPromise<ILocalExtension[]>; getInstalled(type?: LocalExtensionType): TPromise<ILocalExtension[]>;
getExtensionsReport(): TPromise<IReportedExtension[]>; getExtensionsReport(): TPromise<IReportedExtension[]>;
@ -308,7 +308,7 @@ export interface IExtensionEnablementService {
/** /**
* Returns the enablement state for the given extension * Returns the enablement state for the given extension
*/ */
getEnablementState(identifier: IExtensionIdentifier): EnablementState; getEnablementState(extension: ILocalExtension): EnablementState;
/** /**
* Returns `true` if the enablement can be changed. * Returns `true` if the enablement can be changed.
@ -318,7 +318,7 @@ export interface IExtensionEnablementService {
/** /**
* Returns `true` if the given extension identifier is enabled. * Returns `true` if the given extension identifier is enabled.
*/ */
isEnabled(identifier: IExtensionIdentifier): boolean; isEnabled(extension: ILocalExtension): boolean;
/** /**
* Enable or disable the given extension. * 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 * Throws error if enablement is requested for workspace and there is no workspace
*/ */
setEnablement(extension: ILocalExtension, state: EnablementState): TPromise<boolean>; 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'); 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:onDidInstallExtension'): TPromise<void>;
call(command: 'event:onUninstallExtension'): TPromise<void>; call(command: 'event:onUninstallExtension'): TPromise<void>;
call(command: 'event:onDidUninstallExtension'): TPromise<void>; call(command: 'event:onDidUninstallExtension'): TPromise<void>;
call(command: 'install', path: string): TPromise<void>; call(command: 'install', path: string): TPromise<ILocalExtension>;
call(command: 'installFromGallery', extension: IGalleryExtension): TPromise<void>; call(command: 'installFromGallery', extension: IGalleryExtension): TPromise<ILocalExtension>;
call(command: 'uninstall', args: [ILocalExtension, boolean]): TPromise<void>; call(command: 'uninstall', args: [ILocalExtension, boolean]): TPromise<void>;
call(command: 'reinstall', args: [ILocalExtension]): TPromise<ILocalExtension>;
call(command: 'getInstalled'): TPromise<ILocalExtension[]>; call(command: 'getInstalled'): TPromise<ILocalExtension[]>;
call(command: 'getExtensionsReport'): TPromise<IReportedExtension[]>; call(command: 'getExtensionsReport'): TPromise<IReportedExtension[]>;
call(command: string, arg?: any): TPromise<any>; call(command: string, arg?: any): TPromise<any>;
@ -73,11 +74,11 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
private _onDidUninstallExtension = eventFromCall<DidUninstallExtensionEvent>(this.channel, 'event:onDidUninstallExtension'); private _onDidUninstallExtension = eventFromCall<DidUninstallExtensionEvent>(this.channel, 'event:onDidUninstallExtension');
get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this._onDidUninstallExtension; } get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this._onDidUninstallExtension; }
install(zipPath: string): TPromise<void> { install(zipPath: string): TPromise<ILocalExtension> {
return this.channel.call('install', zipPath); return this.channel.call('install', zipPath);
} }
installFromGallery(extension: IGalleryExtension): TPromise<void> { installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
return this.channel.call('installFromGallery', [extension]); return this.channel.call('installFromGallery', [extension]);
} }
@ -85,7 +86,7 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
return this.channel.call('uninstall', [extension, force]); return this.channel.call('uninstall', [extension, force]);
} }
reinstall(extension: ILocalExtension): TPromise<void> { reinstall(extension: ILocalExtension): TPromise<ILocalExtension> {
return this.channel.call('reinstall', [extension]); 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 { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { localizeManifest } from '../common/extensionNls'; import { localizeManifest } from '../common/extensionNls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; 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 Event, { Emitter } from 'vs/base/common/event';
import * as semver from 'semver'; import * as semver from 'semver';
import URI from 'vs/base/common/uri'; import URI from 'vs/base/common/uri';
@ -49,8 +49,6 @@ const INSTALL_ERROR_GALLERY = 'gallery';
const INSTALL_ERROR_LOCAL = 'local'; const INSTALL_ERROR_LOCAL = 'local';
const INSTALL_ERROR_EXTRACTING = 'extracting'; const INSTALL_ERROR_EXTRACTING = 'extracting';
const INSTALL_ERROR_DELETING = 'deleting'; const INSTALL_ERROR_DELETING = 'deleting';
const INSTALL_ERROR_READING_EXTENSION_FROM_DISK = 'readingExtension';
const INSTALL_ERROR_SAVING_METADATA = 'savingMetadata';
const INSTALL_ERROR_UNKNOWN = 'unknown'; const INSTALL_ERROR_UNKNOWN = 'unknown';
export class ExtensionManagementError extends Error { export class ExtensionManagementError extends Error {
@ -141,7 +139,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService)); this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService));
} }
install(zipPath: string): TPromise<void> { install(zipPath: string): TPromise<ILocalExtension> {
zipPath = path.resolve(zipPath); zipPath = path.resolve(zipPath);
return validateLocalExtension(zipPath) return validateLocalExtension(zipPath)
@ -159,7 +157,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
metadata => this.installFromZipPath(identifier, zipPath, metadata, manifest), metadata => this.installFromZipPath(identifier, zipPath, metadata, manifest),
error => this.installFromZipPath(identifier, zipPath, null, manifest)) error => this.installFromZipPath(identifier, zipPath, null, manifest))
.then( .then(
() => this.logService.info('Successfully installed the extension:', identifier.id), local => { this.logService.info('Successfully installed the extension:', identifier.id); return local; },
e => { e => {
this.logService.error('Failed to install the extension:', identifier.id, e.message); this.logService.error('Failed to install the extension:', identifier.id, e.message);
return TPromise.wrapError(e); 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 }) return this.installExtension({ zipPath, id: identifier.id, metadata })
.then(local => { .then(local => {
if (this.galleryService.isEnabled() && local.manifest.extensionDependencies && local.manifest.extensionDependencies.length) { if (this.galleryService.isEnabled() && local.manifest.extensionDependencies && local.manifest.extensionDependencies.length) {
@ -222,12 +220,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
return local; return local;
}) })
.then( .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); } error => { this._onDidInstallExtension.fire({ identifier, zipPath, error }); return TPromise.wrapError(error); }
); );
} }
installFromGallery(extension: IGalleryExtension): TPromise<void> { installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
this.onInstallExtensions([extension]); this.onInstallExtensions([extension]);
return this.collectExtensionsToInstall(extension) return this.collectExtensionsToInstall(extension)
.then( .then(
@ -237,13 +235,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
} }
return this.downloadAndInstallExtensions(extensionsToInstall) return this.downloadAndInstallExtensions(extensionsToInstall)
.then( .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)); errors => this.onDidInstallExtensions(extensionsToInstall, [], errors));
}, },
error => this.onDidInstallExtensions([extension], [], [error])); error => this.onDidInstallExtensions([extension], [], [error]));
} }
reinstall(extension: ILocalExtension): TPromise<void> { reinstall(extension: ILocalExtension): TPromise<ILocalExtension> {
if (!this.galleryService.isEnabled()) { if (!this.galleryService.isEnabled()) {
return TPromise.wrapError(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"))); 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), () => 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)))))); 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> { private extractAndInstall({ zipPath, id, metadata }: InstallableExtension): TPromise<ILocalExtension> {
const extensionPath = path.join(this.extensionsPath, id); const extractPath = path.join(this.extensionsPath, `.${id}`); // Extract to temp path
return pfs.rimraf(extensionPath) return this.extract(id, zipPath, extractPath, { sourcePath: 'extension', overwrite: true })
.then(() => { .then(() => this.completeInstall(id, extractPath))
this.logService.trace(`Started extracting the extension from ${zipPath} to ${extensionPath}`); .then(() => this.scanExtension(id, this.extensionsPath, LocalExtensionType.User))
return extract(zipPath, extensionPath, { sourcePath: 'extension', overwrite: true }) .then(local => {
.then( if (metadata) {
() => { local.metadata = metadata;
this.logService.info(`Extracted extension to ${extensionPath}:`, id); return this.saveMetadataForLocalExtension(local);
return this.completeInstall(id, extensionPath, metadata); }
}, return local;
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)));
} }
private completeInstall(id: string, extensionPath: string, metadata: IGalleryMetadata): TPromise<ILocalExtension> { private extract(id: string, zipPath: string, extractPath: string, options: any): TPromise<void> {
return TPromise.join([readManifest(extensionPath), pfs.readdir(extensionPath)]) this.logService.trace(`Started extracting the extension from ${zipPath} to ${extractPath}`);
.then(null, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_READING_EXTENSION_FROM_DISK))) return pfs.rimraf(extractPath)
.then(([{ manifest }, children]) => { .then(
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0]; () => extract(zipPath, extractPath, options)
const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)).toString() : null; .then(
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; () => this.logService.info(`Extracted extension to ${extractPath}:`, id),
const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)).toString() : null; e => always(pfs.rimraf(extractPath), () => null)
const type = LocalExtensionType.User; .then(() => TPromise.wrapError(new ExtensionManagementError(e.message, INSTALL_ERROR_EXTRACTING)))),
const identifier = { id, uuid: metadata ? metadata.id : null }; e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
}
const local: ILocalExtension = { type, identifier, manifest, metadata, path: extensionPath, readmeUrl, changelogUrl }; private completeInstall(id: string, extractPath: string): TPromise<void> {
return pfs.rename(extractPath, path.join(this.extensionsPath, id))
this.logService.trace(`Updating metadata of the extension:`, id); .then(
return this.saveMetadataForLocalExtension(local) () => this.logService.info('Installation compelted.', id),
.then(() => { e => {
this.logService.info(`Updated metadata of the extension:`, id); this.logService.info('Deleting the extracted extension', id);
return local; return always(pfs.rimraf(extractPath), () => null)
}, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_SAVING_METADATA))); .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> { 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); const extensionPath = path.join(root, folderName);
return pfs.readdir(extensionPath) return pfs.readdir(extensionPath)
.then(children => readManifest(extensionPath) .then(children => readManifest(extensionPath)
@ -855,4 +853,4 @@ export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, ver
export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string { export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string {
return getLocalExtensionId(getGalleryExtensionId(manifest.publisher, manifest.name), manifest.version); 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', () => { test('test state of globally disabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) 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', () => { test('test state of globally enabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) .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', () => { test('test disable an extension for workspace', () => {
@ -126,59 +126,59 @@ suite('ExtensionEnablementService Test', () => {
test('test state of workspace disabled extension', () => { test('test state of workspace disabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) 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', () => { test('test state of workspace and globally disabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) .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', () => { test('test state of workspace enabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) .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', () => { test('test state of globally disabled and workspace enabled extension', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled))
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) .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', () => { test('test state of an extension when disabled for workspace from workspace enabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) .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', () => { test('test state of an extension when disabled globally from workspace enabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) .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', () => { test('test state of an extension when disabled globally from workspace disabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) .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', () => { test('test state of an extension when enabled globally from workspace enabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) .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', () => { test('test state of an extension when enabled globally from workspace disabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) .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', () => { 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', () => { test('test isEnabled return false extension is disabled globally', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) 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', () => { test('test isEnabled return false extension is disabled in workspace', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) 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', () => { test('test isEnabled return true extension is not disabled', () => {
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
.then(() => testObject.setEnablement(aLocalExtension('pub.c'), EnablementState.Disabled)) .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', () => { test('test canChangeEnablement return false for language packs', () => {

View file

@ -44,7 +44,7 @@ export interface INotification {
* close automatically when invoking a secondary action. * close automatically when invoking a secondary action.
* *
* **Note:** If your intent is to show a message with actions to the user, consider * **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! * this usecase and much easier to use!
*/ */
actions?: INotificationActions; actions?: INotificationActions;
@ -129,7 +129,7 @@ export interface INotificationService {
* can be used to control the notification afterwards. * can be used to control the notification afterwards.
* *
* **Note:** If your intent is to show a message with actions to the user, consider * **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! * this usecase and much easier to use!
*/ */
notify(notification: INotification): INotificationHandle; 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 { getProxyAgent } from 'vs/base/node/proxy';
import { IRequestService, IHTTPConfiguration } from 'vs/platform/request/node/request'; import { IRequestService, IHTTPConfiguration } from 'vs/platform/request/node/request';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; 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 * 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 Uninitialized = { type: StateType.Uninitialized };
export type Idle = { type: StateType.Idle }; 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 AvailableForDownload = { type: StateType.AvailableForDownload, update: IUpdate };
export type Downloading = { type: StateType.Downloading, update: IUpdate }; export type Downloading = { type: StateType.Downloading, update: IUpdate };
export type Downloaded = { type: StateType.Downloaded, update: IUpdate }; export type Downloaded = { type: StateType.Downloaded, update: IUpdate };
@ -62,7 +62,7 @@ export type State = Uninitialized | Idle | CheckingForUpdates | AvailableForDown
export const State = { export const State = {
Uninitialized: { type: StateType.Uninitialized } as Uninitialized, Uninitialized: { type: StateType.Uninitialized } as Uninitialized,
Idle: { type: StateType.Idle } as Idle, 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), AvailableForDownload: (update: IUpdate) => ({ type: StateType.AvailableForDownload, update } as AvailableForDownload),
Downloading: (update: IUpdate) => ({ type: StateType.Downloading, update } as Downloading), Downloading: (update: IUpdate) => ({ type: StateType.Downloading, update } as Downloading),
Downloaded: (update: IUpdate) => ({ type: StateType.Downloaded, update } as Downloaded), Downloaded: (update: IUpdate) => ({ type: StateType.Downloaded, update } as Downloaded),
@ -85,7 +85,7 @@ export interface IUpdateService {
readonly onStateChange: Event<State>; readonly onStateChange: Event<State>;
readonly state: State; readonly state: State;
checkForUpdates(explicit: boolean): TPromise<void>; checkForUpdates(context: any): TPromise<void>;
downloadUpdate(): TPromise<void>; downloadUpdate(): TPromise<void>;
applyUpdate(): TPromise<void>; applyUpdate(): TPromise<void>;
quitAndInstall(): TPromise<void>; quitAndInstall(): TPromise<void>;

View file

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

View file

@ -81,7 +81,7 @@ export abstract class AbstractUpdateService implements IUpdateService {
private scheduleCheckForUpdates(delay = 60 * 60 * 1000): TPromise<void> { private scheduleCheckForUpdates(delay = 60 * 60 * 1000): TPromise<void> {
return TPromise.timeout(delay) return TPromise.timeout(delay)
.then(() => this.checkForUpdates()) .then(() => this.checkForUpdates(null))
.then(update => { .then(update => {
if (update) { if (update) {
// Update found, no need to check more // 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); this.logService.trace('update#checkForUpdates, state = ', this.state.type);
if (this.state.type !== StateType.Idle) { if (this.state.type !== StateType.Idle) {
return TPromise.as(null); 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> { downloadUpdate(): TPromise<void> {
@ -158,5 +158,5 @@ export abstract class AbstractUpdateService implements IUpdateService {
} }
protected abstract setUpdateFeedUrl(quality: string): boolean; 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; return true;
} }
protected doCheckForUpdates(explicit: boolean): void { protected doCheckForUpdates(context: any): void {
this.setState(State.CheckingForUpdates(explicit)); this.setState(State.CheckingForUpdates(context));
electron.autoUpdater.checkForUpdates(); electron.autoUpdater.checkForUpdates();
} }
@ -97,7 +97,7 @@ export class DarwinUpdateService extends AbstractUpdateService {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } "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); this.setState(State.Idle);
} }

View file

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

View file

@ -76,12 +76,12 @@ export class Win32UpdateService extends AbstractUpdateService {
return true; return true;
} }
protected doCheckForUpdates(explicit: boolean): void { protected doCheckForUpdates(context: any): void {
if (!this.url) { if (!this.url) {
return; return;
} }
this.setState(State.CheckingForUpdates(explicit)); this.setState(State.CheckingForUpdates(context));
this.requestService.request({ url: this.url }) this.requestService.request({ url: this.url })
.then<IUpdate>(asJson) .then<IUpdate>(asJson)
@ -92,7 +92,7 @@ export class Win32UpdateService extends AbstractUpdateService {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
} }
*/ */
this.telemetryService.publicLog('update:notAvailable', { explicit }); this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle); this.setState(State.Idle);
return TPromise.as(null); return TPromise.as(null);
@ -137,7 +137,7 @@ export class Win32UpdateService extends AbstractUpdateService {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
} }
*/ */
this.telemetryService.publicLog('update:notAvailable', { explicit }); this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle); 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 product from 'vs/platform/node/product';
import { app } from 'electron'; import { app } from 'electron';
import URI from 'vs/base/common/uri'; 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 { 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 { Schemas } from 'vs/base/common/network';
import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { isWindows } from 'vs/base/common/platform'; 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 { export class WindowsService implements IWindowsService, IDisposable {

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