merge master

This commit is contained in:
Erich Gamma
2018-02-22 16:27:52 +01:00
109 changed files with 1537 additions and 549 deletions
@@ -69,7 +69,6 @@ function autoFixSettingsJSON(willSaveEvent: vscode.TextDocumentWillSaveEvent): v
onError(error: ParseErrorCode, offset: number, length: number): void {
if (error === ParseErrorCode.CommaExpected && lastEndOfSomething > -1) {
const fixPosition = document.positionAt(lastEndOfSomething);
edit.insert(document.uri, fixPosition, ',');
}
@@ -104,31 +103,6 @@ function registerSettingsCompletions(): vscode.Disposable {
});
}
// tslint:disable-next-line:no-unused-variable
function provideContributedLocalesProposals(range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
const contributedLocales: string[] = [];
for (const extension of vscode.extensions.all) {
if (extension.packageJSON && extension.packageJSON['contributes'] && extension.packageJSON['contributes']['localizations'] && extension.packageJSON['contributes']['localizations'].length) {
const localizations: { languageId: string }[] = extension.packageJSON['contributes']['localizations'];
for (const localization of localizations) {
if (contributedLocales.indexOf(localization.languageId) === -1) {
contributedLocales.push(localization.languageId);
}
}
}
}
return contributedLocales.map(locale => {
const text = `"${locale}"`;
const item = new vscode.CompletionItem(text);
item.kind = vscode.CompletionItemKind.Value;
item.insertText = text;
item.range = range;
item.filterText = text;
return item;
});
}
interface IExtensionsContent {
recommendations: string[];
}
@@ -6,9 +6,10 @@
"lib": [
"es2015"
],
"strict": true
"strict": true,
"noUnusedLocals": true
},
"include": [
"src/**/*"
]
}
}
+1 -1
View File
@@ -715,7 +715,7 @@
]
},
"dependencies": {
"vscode-languageclient": "^4.0.0-next.9",
"vscode-languageclient": "4.0.0-next.9",
"vscode-nls": "^3.2.1"
},
"devDependencies": {
+1 -1
View File
@@ -10,7 +10,7 @@
"dependencies": {
"vscode-css-languageservice": "^3.0.6",
"vscode-emmet-helper": "^1.1.34",
"vscode-languageserver": "^4.0.0-next.4"
"vscode-languageserver": "4.0.0-next.4"
},
"devDependencies": {
"@types/mocha": "2.2.33",
+1 -1
View File
@@ -48,7 +48,7 @@ vscode-languageserver-types@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"
vscode-languageserver@^4.0.0-next.4:
vscode-languageserver@4.0.0-next.4:
version "4.0.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0-next.4.tgz#162440b15bedaab07e1676f046e4d9b8578b3d92"
dependencies:
+1 -1
View File
@@ -10,7 +10,7 @@ vscode-jsonrpc@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe"
vscode-languageclient@^4.0.0-next.9:
vscode-languageclient@4.0.0-next.9:
version "4.0.0-next.9"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0-next.9.tgz#2a06568f46ee9de3490f85e227d3740a21a03d3a"
dependencies:
-8
View File
@@ -1051,14 +1051,6 @@ export class CommandCenter {
value = (await repository.getCommit(repository.HEAD.commit)).message;
}
// tslint:disable-next-line:no-unused-variable
const getPreviousCommitMessage = async () => {
//Only return the previous commit message if it's an amend commit and the repo already has a commit
if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) {
return (await repository.getCommit('HEAD')).message;
}
};
return await window.showInputBox({
value,
placeHolder: localize('commit message', "Commit message"),
+1 -2
View File
@@ -78,8 +78,7 @@ function _throttle<T>(fn: Function, key: string): Function {
export const throttle = decorate(_throttle);
// tslint:disable-next-line:no-unused-variable
function _sequentialize<T>(fn: Function, key: string): Function {
function _sequentialize(fn: Function, key: string): Function {
const currentKey = `__$sequence$${key}`;
return function (this: any, ...args: any[]) {
-3
View File
@@ -362,8 +362,6 @@ function getGitErrorCode(stderr: string): string | undefined {
export class Git {
private gitPath: string;
// tslint:disable-next-line:no-unused-variable
private version: string;
private env: any;
private _onOutput = new EventEmitter();
@@ -371,7 +369,6 @@ export class Git {
constructor(options: IGitOptions) {
this.gitPath = options.gitPath;
this.version = options.version;
this.env = options.env || {};
}
+4 -9
View File
@@ -398,8 +398,7 @@ class ProgressManager {
private disposable: IDisposable = EmptyDisposable;
// tslint:disable-next-line:no-unused-variable
constructor(private repository: Repository) {
constructor(repository: Repository) {
const start = onceEvent(filterEvent(repository.onDidChangeOperations, () => repository.operations.shouldShowProgress()));
const end = onceEvent(filterEvent(debounceEvent(repository.onDidChangeOperations, 300), () => !repository.operations.shouldShowProgress()));
@@ -791,9 +790,8 @@ export class Repository implements Disposable {
async buffer(ref: string, filePath: string): Promise<Buffer> {
return await this.run(Operation.Show, async () => {
const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
const configFiles = workspace.getConfiguration('files', Uri.file(filePath));
// tslint:disable-next-line:no-unused-variable
const encoding = configFiles.get<string>('encoding');
// const configFiles = workspace.getConfiguration('files', Uri.file(filePath));
// const encoding = configFiles.get<string>('encoding');
// TODO@joao: REsource config api
return await this.repository.buffer(`${ref}:${relativePath}`);
@@ -1011,11 +1009,8 @@ export class Repository implements Disposable {
case 'UU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_MODIFIED, useIcons));
}
// tslint:disable-next-line:no-unused-variable
let isModifiedInIndex = false;
switch (raw.x) {
case 'M': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_MODIFIED, useIcons)); isModifiedInIndex = true; break;
case 'M': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_MODIFIED, useIcons)); break;
case 'A': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_ADDED, useIcons)); break;
case 'D': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_DELETED, useIcons)); break;
case 'R': index.push(new Resource(ResourceGroupType.Index, uri, Status.INDEX_RENAMED, useIcons, renameUri)); break;
+2 -1
View File
@@ -10,7 +10,8 @@
"./node_modules/@types"
],
"strict": true,
"experimentalDecorators": true
"experimentalDecorators": true,
"noUnusedLocals": true
},
"include": [
"src/**/*"
+1 -1
View File
@@ -229,7 +229,7 @@
},
"dependencies": {
"vscode-extension-telemetry": "0.0.13",
"vscode-languageclient": "^4.0.0-next.9",
"vscode-languageclient": "4.0.0-next.9",
"vscode-nls": "^3.2.1"
},
"devDependencies": {
+1 -1
View File
@@ -11,7 +11,7 @@
"vscode-css-languageservice": "^3.0.6",
"vscode-emmet-helper": "^1.1.34",
"vscode-html-languageservice": "^2.0.16",
"vscode-languageserver": "^4.0.0-next.4",
"vscode-languageserver": "4.0.0-next.4",
"vscode-languageserver-types": "^3.6.0-next.1",
"vscode-nls": "^3.2.1",
"vscode-uri": "^1.0.1"
+1 -1
View File
@@ -56,7 +56,7 @@ vscode-languageserver-types@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"
vscode-languageserver@^4.0.0-next.4:
vscode-languageserver@4.0.0-next.4:
version "4.0.0-next.4"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0-next.4.tgz#162440b15bedaab07e1676f046e4d9b8578b3d92"
dependencies:
+1 -1
View File
@@ -38,7 +38,7 @@ vscode-jsonrpc@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe"
vscode-languageclient@^4.0.0-next.9:
vscode-languageclient@4.0.0-next.9:
version "4.0.0-next.9"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0-next.9.tgz#2a06568f46ee9de3490f85e227d3740a21a03d3a"
dependencies:
+64 -2
View File
@@ -8,8 +8,8 @@ import * as path from 'path';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration } from 'vscode';
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient';
import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration, TextDocument, FoldingRangeList as VSFoldingRangeList, FoldingRange as VSFoldingRange } from 'vscode';
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, TextDocumentIdentifier } from 'vscode-languageclient';
import TelemetryReporter from 'vscode-extension-telemetry';
import { hash } from './utils/hash';
@@ -30,6 +30,57 @@ namespace SchemaAssociationNotification {
export const type: NotificationType<ISchemaAssociations, any> = new NotificationType('json/schemaAssociations');
}
interface FoldingRangeList {
/**
* The folding ranges.
*/
ranges: FoldingRange[];
}
export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}
interface FoldingRange {
/**
* The start line number
*/
startLine: number;
/**
* The end line number
*/
endLine: number;
/**
* The actual color value for this color range.
*/
type?: FoldingRangeType;
}
interface FoldingRangeRequest {
/**
* The text document.
*/
textDocument: TextDocumentIdentifier;
}
namespace FoldingRangesRequest {
export const type: RequestType<FoldingRangeRequest, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
}
interface IPackageInfo {
name: string;
version: string;
@@ -124,6 +175,17 @@ export function activate(context: ExtensionContext) {
toDispose.push(workspace.onDidCloseTextDocument(d => handleContentChange(d.uri)));
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context));
languages.registerFoldingProvider(documentSelector, {
provideFoldingRanges(document: TextDocument) {
return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }).then(res => {
if (res && Array.isArray(res.ranges)) {
return new VSFoldingRangeList(res.ranges.map(r => new VSFoldingRange(r.startLine, r.endLine, r.type)));
}
return null;
});
}
});
});
let languageConfiguration: LanguageConfiguration = {
+1 -1
View File
@@ -165,7 +165,7 @@
},
"dependencies": {
"vscode-extension-telemetry": "0.0.13",
"vscode-languageclient": "^4.0.0-next.9",
"vscode-languageclient": "4.0.0-next.9",
"vscode-nls": "^3.2.1"
},
"devDependencies": {
+1 -1
View File
@@ -11,7 +11,7 @@
"jsonc-parser": "^1.0.1",
"request-light": "^0.2.2",
"vscode-json-languageservice": "^3.0.7",
"vscode-languageserver": "^4.0.0-next.3",
"vscode-languageserver": "4.0.0-next.3",
"vscode-nls": "^3.2.1",
"vscode-uri": "^1.0.1"
},
+130 -2
View File
@@ -7,7 +7,7 @@
import {
createConnection, IConnection,
TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentIdentifier
} from 'vscode-languageserver';
import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';
@@ -20,6 +20,7 @@ import Strings = require('./utils/strings');
import { formatError, runSafe, runSafeAsync } from './utils/errors';
import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration } from 'vscode-json-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { createScanner, SyntaxKind } from 'jsonc-parser';
interface ISchemaAssociations {
[pattern: string]: string[];
@@ -37,6 +38,57 @@ namespace SchemaContentChangeNotification {
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
}
interface FoldingRangeList {
/**
* The folding ranges.
*/
ranges: FoldingRange[];
}
export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}
interface FoldingRange {
/**
* The start line number
*/
startLine: number;
/**
* The end line number
*/
endLine: number;
/**
* The actual color value for this color range.
*/
type?: FoldingRangeType | string;
}
interface FoldingRangeRequest {
/**
* The text document.
*/
textDocument: TextDocumentIdentifier;
}
namespace FoldingRangesRequest {
export const type: RequestType<FoldingRangeRequest, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
}
// Create a connection for the server
let connection: IConnection = createConnection();
@@ -347,7 +399,83 @@ connection.onRequest(ColorPresentationRequest.type, params => {
return languageService.getColorPresentations(document, jsonDocument, params.color, params.range);
}
return [];
}, [], `Error while computing color presentationsd for ${params.textDocument.uri}`);
}, [], `Error while computing color presentations for ${params.textDocument.uri}`);
});
connection.onRequest(FoldingRangesRequest.type, params => {
return runSafe(() => {
let document = documents.get(params.textDocument.uri);
if (document) {
let ranges: FoldingRange[] = [];
let stack: FoldingRange[] = [];
let prevStart = -1;
let scanner = createScanner(document.getText(), false);
let token = scanner.scan();
while (token !== SyntaxKind.EOF) {
switch (token) {
case SyntaxKind.OpenBraceToken:
case SyntaxKind.OpenBracketToken: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
stack.push(range);
}
break;
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CloseBracketToken: {
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
if (stack.length > 0 && stack[stack.length - 1].type === type) {
let range = stack.pop();
let line = document.positionAt(scanner.getTokenOffset()).line;
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
range.endLine = line - 1;
ranges.push(range);
prevStart = range.startLine;
}
}
}
break;
case SyntaxKind.BlockCommentTrivia: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (startLine < endLine) {
ranges.push({ startLine, endLine, type: FoldingRangeType.Comment });
prevStart = startLine;
}
}
break;
case SyntaxKind.LineCommentTrivia: {
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
if (m) {
let line = document.positionAt(scanner.getTokenOffset()).line;
if (m[1]) { // start pattern match
let range = { startLine: line, endLine: line, type: FoldingRangeType.Region };
stack.push(range);
} else {
let i = stack.length - 1;
while (i >= 0 && stack[i].type !== FoldingRangeType.Region) {
i--;
}
if (i >= 0) {
let range = stack[i];
stack.length = i;
if (line > range.startLine && prevStart !== range.startLine) {
range.endLine = line;
ranges.push(range);
prevStart = range.startLine;
}
}
}
}
}
break;
}
token = scanner.scan();
}
return <FoldingRangeList>{ ranges };
}
return null;
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`);
});
// Listen on the connection
+1 -1
View File
@@ -88,7 +88,7 @@ vscode-languageserver-types@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3"
vscode-languageserver@^4.0.0-next.3:
vscode-languageserver@4.0.0-next.3:
version "4.0.0-next.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.0.0-next.3.tgz#89a9ce5078e3a86a78e3551c3766194ce4295611"
dependencies:
+1 -1
View File
@@ -38,7 +38,7 @@ vscode-jsonrpc@^3.6.0-next.1:
version "3.6.0-next.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.0-next.1.tgz#3cb463dffe5842d6aec16718ca9252708cd6aabe"
vscode-languageclient@^4.0.0-next.9:
vscode-languageclient@4.0.0-next.9:
version "4.0.0-next.9"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.0.0-next.9.tgz#2a06568f46ee9de3490f85e227d3740a21a03d3a"
dependencies:
-5
View File
@@ -24,11 +24,6 @@
}
],
"grammars": [
{
"language": "Log",
"scopeName": "text.log",
"path": "./syntaxes/log.tmLanguage.json"
},
{
"language": "log",
"scopeName": "text.log",
+2 -3
View File
@@ -27,10 +27,9 @@
if (!unloadedStyles.length) {
return;
}
const args = [unloadedStyles];
window.parent.postMessage({
command: 'did-click-link',
data: `command:_markdown.onPreviewStyleLoadError?${encodeURIComponent(JSON.stringify(args))}`
command: '_markdown.onPreviewStyleLoadError',
args: [unloadedStyles]
}, '*');
});
}());
@@ -354,7 +354,10 @@ export class MarkdownPreviewWebviewManager {
const view = vscode.window.createWebview(
localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)),
viewColumn,
{ enableScripts: true });
{
enableScripts: true,
localResourceRoots: this.getLocalResourceRoots(resource)
});
this.contentProvider.provideTextDocumentContent(resource, this.previewConfigurations).then(x => view.html = x);
@@ -373,4 +376,19 @@ export class MarkdownPreviewWebviewManager {
this.webviews.set(resource.fsPath, view);
return view;
}
private getLocalResourceRoots(
resource: vscode.Uri
): vscode.Uri[] {
const folder = vscode.workspace.getWorkspaceFolder(resource);
if (folder) {
return [folder.uri];
}
if (!resource.scheme || resource.scheme === 'file') {
return [vscode.Uri.parse(path.dirname(resource.fsPath))];
}
return [];
}
}
-1
View File
@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//tslint:disable
'use strict';
import * as path from 'path';
+1 -1
View File
@@ -46,7 +46,7 @@
"vscode-debugprotocol": "1.25.0",
"vscode-ripgrep": "^0.7.1-patch.1.5",
"vscode-textmate": "^3.2.0",
"vscode-xterm": "3.2.0-beta3",
"vscode-xterm": "3.2.0-beta4",
"yauzl": "2.8.0"
},
"devDependencies": {
+48 -11
View File
@@ -120,6 +120,11 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.scrollableElement.onScroll(this.onScroll, this, this.disposables);
domEvent(this.rowsContainer, TouchEventType.Change)(this.onTouchChange, this, this.disposables);
// Prevent the monaco-scrollable-element from scrolling
// https://github.com/Microsoft/vscode/issues/44181
domEvent(this.scrollableElement.getDomNode(), 'scroll')
(e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables);
const onDragOver = mapEvent(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e));
onDragOver(this.onDragOver, this, this.disposables);
@@ -150,7 +155,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const removeRange = intersect(previousRenderRange, deleteRange);
for (let i = removeRange.start; i < removeRange.end; i++) {
this.removeItemFromDOM(this.items[i]);
this.removeItemFromDOM(i);
}
const previousRestRange: IRange = { start: start + deleteCount, end: this.items.length };
@@ -183,19 +188,20 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const removeRange = removeRanges[r];
for (let i = removeRange.start; i < removeRange.end; i++) {
this.removeItemFromDOM(this.items[i]);
this.removeItemFromDOM(i);
}
}
const unrenderedRestRanges = previousUnrenderedRestRanges.map(r => shift(r, delta));
const elementsRange = { start, end: start + elements.length };
const insertRanges = [elementsRange, ...unrenderedRestRanges].map(r => intersect(renderRange, r));
const beforeElement = this.getNextToLastElement(insertRanges);
for (let r = 0; r < insertRanges.length; r++) {
const insertRange = insertRanges[r];
for (let i = insertRange.start; i < insertRange.end; i++) {
this.insertItemInDOM(this.items[i], i);
this.insertItemInDOM(i, beforeElement);
}
}
@@ -254,16 +260,17 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
const rangesToInsert = relativeComplement(renderRange, previousRenderRange);
const rangesToRemove = relativeComplement(previousRenderRange, renderRange);
const beforeElement = this.getNextToLastElement(rangesToInsert);
for (const range of rangesToInsert) {
for (let i = range.start; i < range.end; i++) {
this.insertItemInDOM(this.items[i], i);
this.insertItemInDOM(i, beforeElement);
}
}
for (const range of rangesToRemove) {
for (let i = range.start; i < range.end; i++) {
this.removeItemFromDOM(this.items[i], );
this.removeItemFromDOM(i);
}
}
@@ -281,28 +288,38 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
// DOM operations
private insertItemInDOM(item: IItem<T>, index: number): void {
private insertItemInDOM(index: number, beforeElement: HTMLElement | null): void {
const item = this.items[index];
if (!item.row) {
item.row = this.cache.alloc(item.templateId);
}
if (!item.row.domNode.parentElement) {
this.rowsContainer.appendChild(item.row.domNode);
if (beforeElement) {
this.rowsContainer.insertBefore(item.row.domNode, beforeElement);
} else {
this.rowsContainer.appendChild(item.row.domNode);
}
}
const renderer = this.renderers.get(item.templateId);
item.row.domNode.style.top = `${this.elementTop(index)}px`;
item.row.domNode.style.height = `${item.size}px`;
item.row.domNode.setAttribute('data-index', `${index}`);
this.updateItemInDOM(item, index);
const renderer = this.renderers.get(item.templateId);
renderer.renderElement(item.element, index, item.row.templateData);
}
private updateItemInDOM(item: IItem<T>, index: number): void {
item.row.domNode.style.top = `${this.elementTop(index)}px`;
item.row.domNode.setAttribute('data-index', `${index}`);
item.row.domNode.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false');
item.row.domNode.setAttribute('aria-setsize', `${this.length}`);
item.row.domNode.setAttribute('aria-posinset', `${index + 1}`);
}
private removeItemFromDOM(item: IItem<T>): void {
private removeItemFromDOM(index: number): void {
const item = this.items[index];
this.cache.release(item.row);
item.row = null;
}
@@ -450,6 +467,26 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
};
}
private getNextToLastElement(ranges: IRange[]): HTMLElement | null {
const lastRange = ranges[ranges.length - 1];
if (!lastRange) {
return null;
}
const nextToLastItem = this.items[lastRange.end];
if (!nextToLastItem) {
return null;
}
if (!nextToLastItem.row) {
return null;
}
return nextToLastItem.row.domNode;
}
// Dispose
dispose() {
+22 -33
View File
@@ -21,6 +21,7 @@ import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ISpliceable } from 'vs/base/common/sequence';
import { clamp } from 'vs/base/common/numbers';
export interface IIdentityProvider<T> {
(element: T): string;
@@ -203,32 +204,6 @@ class FocusTrait<T> extends Trait<T> {
}
}
class Aria<T> implements IRenderer<T, HTMLElement>, ISpliceable<T> {
private length = 0;
get templateId(): string {
return 'aria';
}
splice(start: number, deleteCount: number, elements: T[]): void {
this.length += elements.length - deleteCount;
}
renderTemplate(container: HTMLElement): HTMLElement {
return container;
}
renderElement(element: T, index: number, container: HTMLElement): void {
container.setAttribute('aria-setsize', `${this.length}`);
container.setAttribute('aria-posinset', `${index + 1}`);
}
disposeTemplate(container: HTMLElement): void {
// noop
}
}
/**
* The TraitSpliceable is used as a util class to be able
* to preserve traits across splice calls, given an identity
@@ -781,13 +756,12 @@ export class List<T> implements ISpliceable<T>, IDisposable {
renderers: IRenderer<T, any>[],
options: IListOptions<T> = DefaultOptions
) {
const aria = new Aria();
this.focus = new FocusTrait(i => this.getElementDomId(i));
this.selection = new Trait('selected');
mixin(options, defaultStyles, false);
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [aria, this.focus.renderer, this.selection.renderer, r]));
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [this.focus.renderer, this.selection.renderer, r]));
this.view = new ListView(container, delegate, renderers, options);
this.view.domNode.setAttribute('role', 'tree');
@@ -797,7 +771,6 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.styleElement = DOM.createStyleSheet(this.view.domNode);
this.spliceable = new CombinedSpliceable([
aria,
new TraitSpliceable(this.focus, this.view, options.identityProvider),
new TraitSpliceable(this.selection, this.view, options.identityProvider),
this.view
@@ -980,12 +953,9 @@ export class List<T> implements ISpliceable<T>, IDisposable {
const elementHeight = this.view.elementHeight(index);
if (isNumber(relativeTop)) {
relativeTop = relativeTop < 0 ? 0 : relativeTop;
relativeTop = relativeTop > 1 ? 1 : relativeTop;
// y = mx + b
const m = elementHeight - this.view.renderHeight;
this.view.setScrollTop(m * relativeTop + elementTop);
this.view.setScrollTop(m * clamp(relativeTop, 0, 1) + elementTop);
} else {
const viewItemBottom = elementTop + elementHeight;
const wrapperBottom = scrollTop + this.view.renderHeight;
@@ -998,6 +968,24 @@ export class List<T> implements ISpliceable<T>, IDisposable {
}
}
/**
* Returns the relative position of an element rendered in the list.
* Returns `null` if the element isn't *entirely* in the visible viewport.
*/
getRelativeTop(index: number): number | null {
const scrollTop = this.view.getScrollTop();
const elementTop = this.view.elementTop(index);
const elementHeight = this.view.elementHeight(index);
if (elementTop < scrollTop || elementTop + elementHeight > scrollTop + this.view.renderHeight) {
return null;
}
// y = mx + b
const m = elementHeight - this.view.renderHeight;
return Math.abs((scrollTop - elementTop) / m);
}
private getElementDomId(index: number): string {
return `${this.idPrefix}_${index}`;
}
@@ -1097,6 +1085,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
if (focus.length > 0) {
this.view.domNode.setAttribute('aria-activedescendant', this.getElementDomId(focus[0]));
console.log(this.getRelativeTop(focus[0]));
} else {
this.view.domNode.removeAttribute('aria-activedescendant');
}
@@ -49,6 +49,10 @@ export abstract class Panel implements IView {
private _onDidChange = new Emitter<number | undefined>();
readonly onDidChange: Event<number | undefined> = this._onDidChange.event;
get element(): HTMLElement {
return this.el;
}
get draggableElement(): HTMLElement {
return this.header;
}
+2 -2
View File
@@ -180,7 +180,7 @@ export class Delayer<T> {
private timeout: number;
private completionPromise: Promise;
private onSuccess: ValueCallback;
private task: ITask<T>;
private task: ITask<T | TPromise<T>>;
constructor(public defaultDelay: number) {
this.timeout = null;
@@ -189,7 +189,7 @@ export class Delayer<T> {
this.task = null;
}
trigger(task: ITask<T>, delay: number = this.defaultDelay): TPromise<T> {
trigger(task: ITask<T | TPromise<T>>, delay: number = this.defaultDelay): TPromise<T> {
this.task = task;
this.cancelTimeout();
+6 -1
View File
@@ -16,7 +16,7 @@ import strings = require('vs/base/common/strings');
import extfs = require('vs/base/node/extfs');
import { onError } from 'vs/base/test/common/utils';
import { Readable } from 'stream';
import { isLinux } from 'vs/base/common/platform';
import { isLinux, isWindows } from 'vs/base/common/platform';
const ignore = () => { };
@@ -75,6 +75,11 @@ suite('Extfs', () => {
});
test('stat link', function (done: () => void) {
if (isWindows) {
// Symlinks are not the same on win, and we can not create them programitically without admin privileges
return done();
}
const id1 = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id1);
const directory = path.join(parentDir, 'extfs', id1);
@@ -356,8 +356,7 @@ export class IssueReporter extends Disposable {
const disableExtensions = document.getElementById('disableExtensions');
disableExtensions.addEventListener('click', () => {
ipcRenderer.send('workbenchCommand', 'workbench.extensions.action.disableAll');
ipcRenderer.send('workbenchCommand', 'workbench.action.reloadWindow');
ipcRenderer.send('workbenchCommand', 'workbench.action.reloadWindowWithExtensionsDisabled');
});
disableExtensions.addEventListener('keydown', (e) => {
@@ -262,6 +262,7 @@ input:disabled {
.workbenchCommand {
cursor: pointer;
}
.workbenchCommand:disabled {
color: #868e96;
cursor: default
+4
View File
@@ -555,6 +555,10 @@ export class CodeWindow implements ICodeWindow {
configuration['extensions-dir'] = cli['extensions-dir'];
}
if (cli) {
configuration['disable-extensions'] = cli['disable-extensions'];
}
configuration.isInitialStartup = false; // since this is a reload
// Load config
+66
View File
@@ -826,6 +826,67 @@ export interface DocumentColorProvider {
provideColorPresentations(model: model.ITextModel, colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable<IColorPresentation[]>;
}
/**
* A provider of colors for editor models.
*/
/**
* @internal
*/
export interface FoldingProvider {
/**
* Provides the color ranges for a specific model.
*/
provideFoldingRanges(model: model.ITextModel, token: CancellationToken): IFoldingRangeList | Thenable<IFoldingRangeList>;
}
/**
* @internal
*/
export interface IFoldingRangeList {
ranges: IFoldingRange[];
}
/**
* @internal
*/
export interface IFoldingRange {
/**
* The start line number
*/
startLineNumber: number;
/**
* The end line number
*/
endLineNumber: number;
/**
* The optional type of the folding range
*/
type?: FoldingRangeType | string;
// auto-collapse
// header span
}
/**
* @internal
*/
export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}
/**
* @internal
*/
@@ -971,6 +1032,11 @@ export const LinkProviderRegistry = new LanguageFeatureRegistry<LinkProvider>();
*/
export const ColorProviderRegistry = new LanguageFeatureRegistry<DocumentColorProvider>();
/**
* @internal
*/
export const FoldingProviderRegistry = new LanguageFeatureRegistry<FoldingProvider>();
/**
* @internal
*/
+27 -9
View File
@@ -19,17 +19,24 @@ import { registerEditorAction, registerEditorContribution, ServicesAccessor, Edi
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines } from 'vs/editor/contrib/folding/foldingModel';
import { FoldingDecorationProvider } from './foldingDecorations';
import { FoldingRegions } from './foldingRanges';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { IMarginData, IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel';
import { IRange } from 'vs/editor/common/core/range';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { computeRanges as computeIndentRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { IndentRangeProvider } from 'vs/editor/contrib/folding/indentRangeProvider';
import { IPosition } from 'vs/editor/common/core/position';
import { FoldingProviderRegistry } from 'vs/editor/common/modes';
import { SyntaxRangeProvider } from './syntaxRangeProvider';
export const ID = 'editor.contrib.folding';
export interface RangeProvider {
compute(editorModel: ITextModel): TPromise<FoldingRegions>;
}
export class FoldingController implements IEditorContribution {
static MAX_FOLDING_REGIONS = 5000;
@@ -48,6 +55,8 @@ export class FoldingController implements IEditorContribution {
private foldingModel: FoldingModel;
private hiddenRangeModel: HiddenRangeModel;
private rangeProvider: RangeProvider;
private foldingModelPromise: TPromise<FoldingModel>;
private updateScheduler: Delayer<FoldingModel>;
@@ -69,6 +78,7 @@ export class FoldingController implements IEditorContribution {
this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls;
this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged()));
this.globalToDispose.push(FoldingProviderRegistry.onDidChange(() => this.onModelChanged()));
this.globalToDispose.push(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => {
if (e.contribInfo) {
@@ -167,17 +177,22 @@ export class FoldingController implements IEditorContribution {
this.foldingModelPromise = null;
this.hiddenRangeModel = null;
this.cursorChangedScheduler = null;
this.rangeProvider = null;
}
});
this.onModelContentChanged();
}
private computeRanges(editorModel: ITextModel) {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
let offSide = foldingRules && foldingRules.offSide;
let markers = foldingRules && foldingRules.markers;
let ranges = computeIndentRanges(editorModel, offSide, markers);
return ranges;
private getRangeProvider(): RangeProvider {
if (!this.rangeProvider) {
let foldingProviders = FoldingProviderRegistry.ordered(this.foldingModel.textModel);
if (foldingProviders.length) {
this.rangeProvider = new SyntaxRangeProvider(foldingProviders);
} else {
this.rangeProvider = new IndentRangeProvider();
}
}
return this.rangeProvider;
}
public getFoldingModel() {
@@ -191,9 +206,12 @@ export class FoldingController implements IEditorContribution {
// some cursors might have moved into hidden regions, make sure they are in expanded regions
let selections = this.editor.getSelections();
let selectionLineNumbers = selections ? selections.map(s => s.startLineNumber) : [];
this.foldingModel.update(this.computeRanges(this.foldingModel.textModel), selectionLineNumbers);
return this.getRangeProvider().compute(this.foldingModel.textModel).then(foldingRanges => {
this.foldingModel.update(foldingRanges, selectionLineNumbers);
return this.foldingModel;
});
}
return this.foldingModel;
return null;
});
}
}
+34 -34
View File
@@ -5,7 +5,7 @@
import { ITextModel, IModelDecorationOptions, IModelDeltaDecoration, IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import Event, { Emitter } from 'vs/base/common/event';
import { FoldingRanges, ILineRange, FoldingRegion } from './foldingRanges';
import { FoldingRegions, ILineRange, FoldingRegion } from './foldingRanges';
export interface IDecorationProvider {
getDecorationOption(isCollapsed: boolean): IModelDecorationOptions;
@@ -24,13 +24,13 @@ export class FoldingModel {
private _textModel: ITextModel;
private _decorationProvider: IDecorationProvider;
private _ranges: FoldingRanges;
private _regions: FoldingRegions;
private _editorDecorationIds: string[];
private _isInitialized: boolean;
private _updateEventEmitter = new Emitter<FoldingModelChangeEvent>();
public get ranges(): FoldingRanges { return this._ranges; }
public get regions(): FoldingRegions { return this._regions; }
public get onDidChange(): Event<FoldingModelChangeEvent> { return this._updateEventEmitter.event; }
public get textModel() { return this._textModel; }
public get isInitialized() { return this._isInitialized; }
@@ -38,7 +38,7 @@ export class FoldingModel {
constructor(textModel: ITextModel, decorationProvider: IDecorationProvider) {
this._textModel = textModel;
this._decorationProvider = decorationProvider;
this._ranges = new FoldingRanges(new Uint32Array(0), new Uint32Array(0));
this._regions = new FoldingRegions(new Uint32Array(0), new Uint32Array(0));
this._editorDecorationIds = [];
this._isInitialized = false;
}
@@ -54,8 +54,8 @@ export class FoldingModel {
let editorDecorationId = this._editorDecorationIds[index];
if (editorDecorationId && !processed[editorDecorationId]) {
processed[editorDecorationId] = true;
let newCollapseState = !this._ranges.isCollapsed(index);
this._ranges.setCollapsed(index, newCollapseState);
let newCollapseState = !this._regions.isCollapsed(index);
this._regions.setCollapsed(index, newCollapseState);
accessor.changeDecorationOptions(editorDecorationId, this._decorationProvider.getDecorationOption(newCollapseState));
}
}
@@ -63,7 +63,7 @@ export class FoldingModel {
this._updateEventEmitter.fire({ model: this, collapseStateChanged: regions });
}
public update(newRanges: FoldingRanges, blockedLineNumers: number[] = []): void {
public update(newRegions: FoldingRegions, blockedLineNumers: number[] = []): void {
let newEditorDecorations = [];
let isBlocked = (startLineNumber, endLineNumber) => {
@@ -76,11 +76,11 @@ export class FoldingModel {
};
let initRange = (index: number, isCollapsed: boolean) => {
let startLineNumber = newRanges.getStartLineNumber(index);
if (isCollapsed && isBlocked(startLineNumber, newRanges.getEndLineNumber(index))) {
let startLineNumber = newRegions.getStartLineNumber(index);
if (isCollapsed && isBlocked(startLineNumber, newRegions.getEndLineNumber(index))) {
isCollapsed = false;
}
newRanges.setCollapsed(index, isCollapsed);
newRegions.setCollapsed(index, isCollapsed);
let maxColumn = this._textModel.getLineMaxColumn(startLineNumber);
let decorationRange = {
startLineNumber: startLineNumber,
@@ -93,8 +93,8 @@ export class FoldingModel {
let i = 0;
let nextCollapsed = () => {
while (i < this._ranges.length) {
let isCollapsed = this._ranges.isCollapsed(i);
while (i < this._regions.length) {
let isCollapsed = this._regions.isCollapsed(i);
i++;
if (isCollapsed) {
return i - 1;
@@ -105,14 +105,14 @@ export class FoldingModel {
let k = 0;
let collapsedIndex = nextCollapsed();
while (collapsedIndex !== -1 && k < newRanges.length) {
while (collapsedIndex !== -1 && k < newRegions.length) {
// get the latest range
let decRange = this._textModel.getDecorationRange(this._editorDecorationIds[collapsedIndex]);
if (decRange) {
let collapsedStartLineNumber = decRange.startLineNumber;
if (this._textModel.getLineMaxColumn(collapsedStartLineNumber) === decRange.startColumn) { // test that the decoration is still at the end otherwise it got deleted
while (k < newRanges.length) {
let startLineNumber = newRanges.getStartLineNumber(k);
while (k < newRegions.length) {
let startLineNumber = newRegions.getStartLineNumber(k);
if (collapsedStartLineNumber >= startLineNumber) {
initRange(k, collapsedStartLineNumber === startLineNumber);
k++;
@@ -124,13 +124,13 @@ export class FoldingModel {
}
collapsedIndex = nextCollapsed();
}
while (k < newRanges.length) {
while (k < newRegions.length) {
initRange(k, false);
k++;
}
this._editorDecorationIds = this._decorationProvider.deltaDecorations(this._editorDecorationIds, newEditorDecorations);
this._ranges = newRanges;
this._regions = newRegions;
this._isInitialized = true;
this._updateEventEmitter.fire({ model: this });
}
@@ -140,12 +140,12 @@ export class FoldingModel {
*/
public getMemento(): CollapseMemento {
let collapsedRanges: ILineRange[] = [];
for (let i = 0; i < this._ranges.length; i++) {
if (this._ranges.isCollapsed(i)) {
for (let i = 0; i < this._regions.length; i++) {
if (this._regions.isCollapsed(i)) {
let range = this._textModel.getDecorationRange(this._editorDecorationIds[i]);
if (range) {
let startLineNumber = range.startLineNumber;
let endLineNumber = range.endLineNumber + this._ranges.getEndLineNumber(i) - this._ranges.getStartLineNumber(i);
let endLineNumber = range.endLineNumber + this._regions.getEndLineNumber(i) - this._regions.getStartLineNumber(i);
collapsedRanges.push({ startLineNumber, endLineNumber });
}
}
@@ -179,11 +179,11 @@ export class FoldingModel {
getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion, level: number) => boolean): FoldingRegion[] {
let result: FoldingRegion[] = [];
if (this._ranges) {
let index = this._ranges.findRange(lineNumber);
if (this._regions) {
let index = this._regions.findRange(lineNumber);
let level = 1;
while (index >= 0) {
let current = this._ranges.toRegion(index);
let current = this._regions.toRegion(index);
if (!filter || filter(current, level)) {
result.push(current);
}
@@ -195,10 +195,10 @@ export class FoldingModel {
}
getRegionAtLine(lineNumber: number): FoldingRegion {
if (this._ranges) {
let index = this._ranges.findRange(lineNumber);
if (this._regions) {
let index = this._regions.findRange(lineNumber);
if (index >= 0) {
return this._ranges.toRegion(index);
return this._regions.toRegion(index);
}
}
return null;
@@ -210,9 +210,9 @@ export class FoldingModel {
let levelStack: FoldingRegion[] = trackLevel ? [] : null;
let index = region ? region.regionIndex + 1 : 0;
let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE;
for (let i = index, len = this._ranges.length; i < len; i++) {
let current = this._ranges.toRegion(i);
if (this._ranges.getStartLineNumber(i) < endLineNumber) {
for (let i = index, len = this._regions.length; i < len; i++) {
let current = this._regions.toRegion(i);
if (this._regions.getStartLineNumber(i) < endLineNumber) {
if (trackLevel) {
while (levelStack.length > 0 && !current.containedBy(levelStack[levelStack.length - 1])) {
levelStack.pop();
@@ -296,13 +296,13 @@ export function setCollapseStateAtLevel(foldingModel: FoldingModel, foldLevel: n
*/
export function setCollapseStateForMatchingLines(foldingModel: FoldingModel, regExp: RegExp, doCollapse: boolean): void {
let editorModel = foldingModel.textModel;
let ranges = foldingModel.ranges;
let regions = foldingModel.regions;
let toToggle = [];
for (let i = ranges.length - 1; i >= 0; i--) {
if (doCollapse !== ranges.isCollapsed(i)) {
let startLineNumber = ranges.getStartLineNumber(i);
for (let i = regions.length - 1; i >= 0; i--) {
if (doCollapse !== regions.isCollapsed(i)) {
let startLineNumber = regions.getStartLineNumber(i);
if (regExp.test(editorModel.getLineContent(startLineNumber))) {
toToggle.push(ranges.toRegion(i));
toToggle.push(regions.toRegion(i));
}
}
}
@@ -15,7 +15,7 @@ export const MAX_LINE_NUMBER = 0xFFFFFF;
const MASK_INDENT = 0xFF000000;
export class FoldingRanges {
export class FoldingRegions {
private _startIndexes: Uint32Array;
private _endIndexes: Uint32Array;
private _collapseStates: Uint32Array;
@@ -138,7 +138,7 @@ export class FoldingRanges {
export class FoldingRegion {
constructor(private ranges: FoldingRanges, private index: number) {
constructor(private ranges: FoldingRegions, private index: number) {
}
public get startLineNumber() {
@@ -23,7 +23,7 @@ export class HiddenRangeModel {
this._foldingModel = model;
this._foldingModelListener = model.onDidChange(_ => this.updateHiddenRanges());
this._hiddenRanges = [];
if (model.ranges.length) {
if (model.regions.length) {
this.updateHiddenRanges();
}
}
@@ -37,7 +37,7 @@ export class HiddenRangeModel {
let lastCollapsedStart = Number.MAX_VALUE;
let lastCollapsedEnd = -1;
let ranges = this._foldingModel.ranges;
let ranges = this._foldingModel.regions;
for (; i < ranges.length; i++) {
if (!ranges.isCollapsed(i)) {
continue;
@@ -7,25 +7,37 @@
import { ITextModel } from 'vs/editor/common/model';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
import { FoldingRanges, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/foldingRanges';
import { FoldingRegions, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/foldingRanges';
import { TextModel } from 'vs/editor/common/model/textModel';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
export class IndentRangeProvider implements RangeProvider {
compute(editorModel: ITextModel): TPromise<FoldingRegions> {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
let offSide = foldingRules && foldingRules.offSide;
let markers = foldingRules && foldingRules.markers;
return TPromise.as(computeRanges(editorModel, offSide, markers));
}
}
// public only for testing
export class RangesCollector {
private _startIndexes: number[];
private _endIndexes: number[];
private _indentOccurrences: number[];
private _length: number;
private _FoldingRangesLimit: number;
private _foldingRangesLimit: number;
constructor(FoldingRangesLimit: number) {
constructor(foldingRangesLimit: number) {
this._startIndexes = [];
this._endIndexes = [];
this._indentOccurrences = [];
this._length = 0;
this._FoldingRangesLimit = FoldingRangesLimit;
this._foldingRangesLimit = foldingRangesLimit;
}
public insertFirst(startLineNumber: number, endLineNumber: number, indent: number) {
@@ -42,7 +54,7 @@ export class RangesCollector {
}
public toIndentRanges(model: ITextModel) {
if (this._length <= this._FoldingRangesLimit) {
if (this._length <= this._foldingRangesLimit) {
// reverse and create arrays of the exact length
let startIndexes = new Uint32Array(this._length);
let endIndexes = new Uint32Array(this._length);
@@ -50,14 +62,14 @@ export class RangesCollector {
startIndexes[k] = this._startIndexes[i];
endIndexes[k] = this._endIndexes[i];
}
return new FoldingRanges(startIndexes, endIndexes);
return new FoldingRegions(startIndexes, endIndexes);
} else {
let entries = 0;
let maxIndent = this._indentOccurrences.length;
for (let i = 0; i < this._indentOccurrences.length; i++) {
let n = this._indentOccurrences[i];
if (n) {
if (n + entries > this._FoldingRangesLimit) {
if (n + entries > this._foldingRangesLimit) {
maxIndent = i;
break;
}
@@ -78,7 +90,7 @@ export class RangesCollector {
k++;
}
}
return new FoldingRanges(startIndexes, endIndexes);
return new FoldingRegions(startIndexes, endIndexes);
}
}
@@ -87,9 +99,9 @@ export class RangesCollector {
interface PreviousRegion { indent: number; line: number; marker: boolean; }
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, FoldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRanges {
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRegions {
const tabSize = model.getOptions().tabSize;
let result = new RangesCollector(FoldingRangesLimit);
let result = new RangesCollector(foldingRangesLimit);
let pattern = void 0;
if (markers) {
@@ -0,0 +1,154 @@
/*---------------------------------------------------------------------------------------------
* 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 { FoldingProvider, IFoldingRange } from 'vs/editor/common/modes';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { asWinJsPromise } from 'vs/base/common/async';
import { ITextModel } from 'vs/editor/common/model';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { MAX_LINE_NUMBER, FoldingRegions } from './foldingRanges';
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
export interface IFoldingRangeData extends IFoldingRange {
rank: number;
}
export class SyntaxRangeProvider implements RangeProvider {
constructor(private providers: FoldingProvider[]) {
}
compute(model: ITextModel): TPromise<FoldingRegions> {
return collectSyntaxRanges(this.providers, model).then(ranges => {
return sanitizeRanges(ranges);
});
}
}
function collectSyntaxRanges(providers: FoldingProvider[], model: ITextModel): TPromise<IFoldingRangeData[]> {
const rangeData: IFoldingRangeData[] = [];
let promises = providers.map((provider, rank) => asWinJsPromise(token => provider.provideFoldingRanges(model, token)).then(list => {
if (list && Array.isArray(list.ranges)) {
for (let r of list.ranges) {
rangeData.push({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, rank, type: r.type });
}
}
}, onUnexpectedExternalError));
return TPromise.join(promises).then(() => {
return rangeData;
});
}
export class RangesCollector {
private _startIndexes: number[];
private _endIndexes: number[];
private _nestingLevels: number[];
private _nestingLevelCounts: number[];
private _length: number;
private _foldingRangesLimit: number;
constructor(foldingRangesLimit: number) {
this._startIndexes = [];
this._endIndexes = [];
this._nestingLevels = [];
this._nestingLevelCounts = [];
this._length = 0;
this._foldingRangesLimit = foldingRangesLimit;
}
public add(startLineNumber: number, endLineNumber: number, nestingLevel: number) {
if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
return;
}
let index = this._length;
this._startIndexes[index] = startLineNumber;
this._endIndexes[index] = endLineNumber;
this._nestingLevels[index] = nestingLevel;
this._length++;
if (nestingLevel < 30) {
this._nestingLevelCounts[nestingLevel] = (this._nestingLevelCounts[nestingLevel] || 0) + 1;
}
}
public toIndentRanges() {
if (this._length <= this._foldingRangesLimit) {
let startIndexes = new Uint32Array(this._length);
let endIndexes = new Uint32Array(this._length);
for (let i = 0; i < this._length; i++) {
startIndexes[i] = this._startIndexes[i];
endIndexes[i] = this._endIndexes[i];
}
return new FoldingRegions(startIndexes, endIndexes);
} else {
let entries = 0;
let maxLevel = this._nestingLevelCounts.length;
for (let i = 0; i < this._nestingLevelCounts.length; i++) {
let n = this._nestingLevelCounts[i];
if (n) {
if (n + entries > this._foldingRangesLimit) {
maxLevel = i;
break;
}
entries += n;
}
}
let startIndexes = new Uint32Array(entries);
let endIndexes = new Uint32Array(entries);
for (let i = 0, k = 0; i < this._length; i++) {
let level = this._nestingLevels[i];
if (level < maxLevel) {
startIndexes[k] = this._startIndexes[i];
endIndexes[k] = this._endIndexes[i];
k++;
}
}
return new FoldingRegions(startIndexes, endIndexes);
}
}
}
export function sanitizeRanges(rangeData: IFoldingRangeData[]): FoldingRegions {
let sorted = rangeData.sort((d1, d2) => {
let diff = d1.startLineNumber - d2.startLineNumber;
if (diff === 0) {
diff = d1.rank - d2.rank;
}
return diff;
});
let collector = new RangesCollector(MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT);
let top: IFoldingRangeData = null;
let previous = [];
for (let entry of sorted) {
if (!top) {
top = entry;
collector.add(entry.startLineNumber, entry.endLineNumber, previous.length);
} else {
if (entry.startLineNumber > top.startLineNumber) {
if (entry.endLineNumber <= top.endLineNumber) {
previous.push(top);
top = entry;
collector.add(entry.startLineNumber, entry.endLineNumber, previous.length);
} else if (entry.startLineNumber > top.endLineNumber) {
do {
top = previous.pop();
} while (top && entry.startLineNumber > top.endLineNumber);
previous.push(top);
top = entry;
collector.add(entry.startLineNumber, entry.endLineNumber, previous.length);
}
}
}
}
return collector.toIndentRanges();
}
@@ -61,7 +61,7 @@ suite('Folding Model', () => {
function assertFoldedRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
let actualRanges = [];
let actual = foldingModel.ranges;
let actual = foldingModel.regions;
for (let i = 0; i < actual.length; i++) {
if (actual.isCollapsed(i)) {
actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i)));
@@ -72,7 +72,7 @@ suite('Folding Model', () => {
function assertRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
let actualRanges = [];
let actual = foldingModel.ranges;
let actual = foldingModel.regions;
for (let i = 0; i < actual.length; i++) {
actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.isCollapsed(i)));
}
@@ -389,6 +389,14 @@ export function registerColorProvider(languageId: string, provider: modes.Docume
return modes.ColorProviderRegistry.register(languageId, provider);
}
/**
* Register a folding provider
*/
/*export function registerFoldingProvider(languageId: string, provider: modes.FoldingProvider): IDisposable {
return modes.FoldingProviderRegistry.register(languageId, provider);
}*/
/**
* Contains additional diagnostic information about the context in which
* a [code action](#CodeActionProvider.provideCodeActions) is run.
+3
View File
@@ -4047,6 +4047,9 @@ declare module monaco.languages {
*/
export function registerColorProvider(languageId: string, provider: DocumentColorProvider): IDisposable;
/**
* Register a folding provider
*/
/**
* Contains additional diagnostic information about the context in which
* a [code action](#CodeActionProvider.provideCodeActions) is run.
@@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------------------------
* 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 { Disposable } from 'vs/base/common/lifecycle';
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { TPromise } from 'vs/base/common/winjs.base';
import { ILogService } from 'vs/platform/log/common/log';
import { fork, ChildProcess } from 'child_process';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { join } from 'vs/base/common/paths';
import { Limiter } from 'vs/base/common/async';
import { fromNodeEventEmitter, anyEvent, mapEvent, debounceEvent } from 'vs/base/common/event';
export class ExtensionsLifecycle extends Disposable {
private processesLimiter: Limiter<void> = new Limiter(5); // Run max 5 processes in parallel
constructor(
@ILogService private logService: ILogService
) {
super();
}
uninstall(extension: ILocalExtension): TPromise<void> {
const uninstallScript = this.parseUninstallScript(extension);
if (uninstallScript) {
this.logService.info(extension.identifier.id, 'Running Uninstall hook');
return this.processesLimiter.queue(() =>
this.runUninstallHook(uninstallScript.uninstallHook, uninstallScript.args, extension)
.then(() => this.logService.info(extension.identifier.id, 'Finished running uninstall hook'), err => this.logService.error(extension.identifier.id, `Failed to run uninstall hook: ${err}`)));
}
return TPromise.as(null);
}
private parseUninstallScript(extension: ILocalExtension): { uninstallHook: string, args: string[] } {
if (extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts']['vscode:uninstall'] === 'string') {
const uninstallScript = (<string>extension.manifest['scripts']['vscode:uninstall']).split(' ');
if (uninstallScript.length < 2 || uninstallScript[0] !== 'node' || !uninstallScript[1]) {
this.logService.warn(extension.identifier.id, 'Uninstall script should be a node script');
return null;
}
return { uninstallHook: join(extension.path, uninstallScript[1]), args: uninstallScript.slice(2) || [] };
}
return null;
}
private runUninstallHook(lifecycleHook: string, args: string[], extension: ILocalExtension): TPromise<void> {
return new TPromise((c, e) => {
const extensionLifecycleProcess = this.start(lifecycleHook, args, extension);
let timeoutHandler;
const onexit = (error?: string) => {
clearTimeout(timeoutHandler);
timeoutHandler = null;
if (error) {
e(error);
} else {
c(null);
}
};
// on error
extensionLifecycleProcess.on('error', (err) => {
if (timeoutHandler) {
onexit(toErrorMessage(err) || 'Unknown');
}
});
// on exit
extensionLifecycleProcess.on('exit', (code: number, signal: string) => {
if (timeoutHandler) {
onexit(code ? `Process exited with code ${code}` : void 0);
}
});
// timeout: kill process after waiting for 5s
timeoutHandler = setTimeout(() => {
timeoutHandler = null;
extensionLifecycleProcess.kill();
e('timed out');
}, 5000);
});
}
private start(uninstallHook: string, args: string[], extension: ILocalExtension): ChildProcess {
const opts = {
silent: true,
execArgv: <string[]>undefined
};
const extensionUninstallProcess = fork(uninstallHook, ['--type=extensionUninstall', ...args], opts);
// Catch all output coming from the process
type Output = { data: string, format: string[] };
extensionUninstallProcess.stdout.setEncoding('utf8');
extensionUninstallProcess.stderr.setEncoding('utf8');
const onStdout = fromNodeEventEmitter<string>(extensionUninstallProcess.stdout, 'data');
const onStderr = fromNodeEventEmitter<string>(extensionUninstallProcess.stderr, 'data');
const onOutput = anyEvent(
mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })),
mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] }))
);
// Debounce all output, so we can render it in the Chrome console as a group
const onDebouncedOutput = debounceEvent<Output>(onOutput, (r, o) => {
return r
? { data: r.data + o.data, format: [...r.format, ...o.format] }
: { data: o.data, format: o.format };
}, 100);
// Print out extension host output
onDebouncedOutput(data => {
console.group(extension.identifier.id);
console.log(data.data, ...data.format);
console.groupEnd();
});
return extensionUninstallProcess;
}
}
@@ -35,6 +35,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle';
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem';
@@ -110,6 +111,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
private lastReportTimestamp = 0;
private readonly installingExtensions: Map<string, TPromise<ILocalExtension>> = new Map<string, TPromise<ILocalExtension>>();
private readonly manifestCache: ExtensionsManifestCache;
private readonly extensionLifecycle: ExtensionsLifecycle;
private readonly _onInstallExtension = new Emitter<InstallExtensionEvent>();
readonly onInstallExtension: Event<InstallExtensionEvent> = this._onInstallExtension.event;
@@ -135,6 +137,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.uninstalledFileLimiter = new Limiter(1);
this._register(toDisposable(() => this.installingExtensions.clear()));
this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this));
this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService));
}
install(zipPath: string): TPromise<void> {
@@ -708,24 +711,43 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
removeDeprecatedExtensions(): TPromise<any> {
return this.removeUninstalledExtensions()
.then(() => this.removeOutdatedExtensions());
}
private removeUninstalledExtensions(): TPromise<void> {
return this.getUninstalledExtensions()
.then(uninstalled => this.scanExtensions(this.extensionsPath, LocalExtensionType.User) // All user extensions
.then(extensions => {
const toRemove: { path: string, id: string }[] = [];
// Uninstalled extensions
toRemove.push(...extensions.filter(e => uninstalled[e.identifier.id]).map(e => ({ path: e.path, id: e.identifier.id })));
// Outdated extensions
const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => ({ id: getGalleryExtensionIdFromLocal(e), uuid: e.identifier.uuid }));
toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1)))
.map(e => ({ path: e.path, id: e.identifier.id })));
return TPromise.join(distinct(toRemove, e => e.path).map(({ path, id }) => {
return pfs.rimraf(path)
.then(() => this.withUninstalledExtensions(uninstalled => delete uninstalled[id]));
}));
const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[e.identifier.id]);
return TPromise.join(toRemove.map(e => this.removeUninstalledExtension(e)));
})
);
).then(() => null);
}
private removeOutdatedExtensions(): TPromise<void> {
return this.scanExtensions(this.extensionsPath, LocalExtensionType.User) // All user extensions
.then(extensions => {
const toRemove: ILocalExtension[] = [];
// Outdated extensions
const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => ({ id: getGalleryExtensionIdFromLocal(e), uuid: e.identifier.uuid }));
toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1))));
return TPromise.join(toRemove.map(extension => this.removeExtension(extension, 'outdated')));
}).then(() => null);
}
private removeUninstalledExtension(extension: ILocalExtension): TPromise<void> {
return this.extensionLifecycle.uninstall(extension)
.then(() => this.removeExtension(extension, 'uninstalled'))
.then(() => this.withUninstalledExtensions(uninstalled => delete uninstalled[extension.identifier.id]))
.then(() => null);
}
private removeExtension(extension: ILocalExtension, type: string): TPromise<void> {
this.logService.trace(extension.identifier.id, 'Deleting from disk', type);
return pfs.rimraf(extension.path).then(() => this.logService.info(extension.identifier.id, 'Deleted from disk'));
}
private isUninstalled(id: string): TPromise<boolean> {
@@ -264,6 +264,10 @@ const schema: IJSONSchema = {
'vscode:prepublish': {
description: nls.localize('vscode.extension.scripts.prepublish', 'Script executed before the package is published as a VS Code extension.'),
type: 'string'
},
'vscode:uninstall': {
description: nls.localize('vscode.extension.scripts.uninstall', 'Script executed after the extension is uninstalled from VS Code. Only Node script is supported.'),
type: 'string'
}
}
},
@@ -51,6 +51,11 @@ export interface IStatusbarEntry {
* An optional extension ID if this entry is provided from an extension.
*/
extensionId?: string;
/**
* Wether to show a beak above the status bar entry.
*/
showBeak?: boolean;
}
export interface IStatusbarService {
@@ -11,7 +11,7 @@ import { readFile } from 'vs/base/node/pfs';
export function resolveCommonProperties(commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string; }> {
const result: { [name: string]: string; } = Object.create(null);
// __GDPR__COMMON__ "common.machineId" : { "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
// __GDPR__COMMON__ "common.machineId" : { "endPoint": "MacAddressHash", "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
result['common.machineId'] = machineId;
// __GDPR__COMMON__ "sessionID" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
result['sessionID'] = uuid.generateUuid() + Date.now();
@@ -5,6 +5,7 @@
'use strict';
import product from 'vs/platform/node/product';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IRequestService } from 'vs/platform/request/node/request';
@@ -76,7 +77,13 @@ export class LinuxUpdateService extends AbstractUpdateService {
}
protected doDownloadUpdate(state: AvailableForDownload): TPromise<void> {
shell.openExternal(state.update.url);
// Use the download URL if available as we don't currently detect the package type that was
// installed and the website download page is more useful than the tarball generally.
if (product.downloadUrl && product.downloadUrl.length > 0) {
shell.openExternal(product.downloadUrl);
} else {
shell.openExternal(state.update.url);
}
this.setState(State.Idle);
return TPromise.as(null);
+2 -2
View File
@@ -108,7 +108,7 @@ export interface IWindowsService {
showSaveDialog(windowId: number, options: SaveDialogOptions): TPromise<string>;
showOpenDialog(windowId: number, options: OpenDialogOptions): TPromise<string[]>;
reloadWindow(windowId: number): TPromise<void>;
reloadWindow(windowId: number, args?: ParsedArgs): TPromise<void>;
openDevTools(windowId: number): TPromise<void>;
toggleDevTools(windowId: number): TPromise<void>;
closeWorkspace(windowId: number): TPromise<void>;
@@ -183,7 +183,7 @@ export interface IWindowService {
pickFileAndOpen(options: INativeOpenDialogOptions): TPromise<void>;
pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void>;
pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise<void>;
reloadWindow(): TPromise<void>;
reloadWindow(args?: ParsedArgs): TPromise<void>;
openDevTools(): TPromise<void>;
toggleDevTools(): TPromise<void>;
closeWorkspace(): TPromise<void>;
+5 -4
View File
@@ -13,6 +13,7 @@ import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceFolde
import { IRecentlyOpened } from 'vs/platform/history/common/history';
import { ICommandAction } from 'vs/platform/actions/common/actions';
import URI from 'vs/base/common/uri';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
export interface IWindowsChannel extends IChannel {
call(command: 'event:onWindowOpen'): TPromise<number>;
@@ -25,7 +26,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'showMessageBox', arg: [number, MessageBoxOptions]): TPromise<IMessageBoxResult>;
call(command: 'showSaveDialog', arg: [number, SaveDialogOptions]): TPromise<string>;
call(command: 'showOpenDialog', arg: [number, OpenDialogOptions]): TPromise<string[]>;
call(command: 'reloadWindow', arg: number): TPromise<void>;
call(command: 'reloadWindow', arg: [number, ParsedArgs]): TPromise<void>;
call(command: 'toggleDevTools', arg: number): TPromise<void>;
call(command: 'closeWorkspace', arg: number): TPromise<void>;
call(command: 'createAndEnterWorkspace', arg: [number, IWorkspaceFolderCreationData[], string]): TPromise<IEnterWorkspaceResult>;
@@ -91,7 +92,7 @@ export class WindowsChannel implements IWindowsChannel {
case 'showMessageBox': return this.service.showMessageBox(arg[0], arg[1]);
case 'showSaveDialog': return this.service.showSaveDialog(arg[0], arg[1]);
case 'showOpenDialog': return this.service.showOpenDialog(arg[0], arg[1]);
case 'reloadWindow': return this.service.reloadWindow(arg);
case 'reloadWindow': return this.service.reloadWindow(arg[0], arg[1]);
case 'openDevTools': return this.service.openDevTools(arg);
case 'toggleDevTools': return this.service.toggleDevTools(arg);
case 'closeWorkspace': return this.service.closeWorkspace(arg);
@@ -192,8 +193,8 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('showOpenDialog', [windowId, options]);
}
reloadWindow(windowId: number): TPromise<void> {
return this.channel.call('reloadWindow', windowId);
reloadWindow(windowId: number, args?: ParsedArgs): TPromise<void> {
return this.channel.call('reloadWindow', [windowId, args]);
}
openDevTools(windowId: number): TPromise<void> {
@@ -11,6 +11,7 @@ import { IWindowService, IWindowsService, INativeOpenDialogOptions, IEnterWorksp
import { IRecentlyOpened } from 'vs/platform/history/common/history';
import { ICommandAction } from 'vs/platform/actions/common/actions';
import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
export class WindowService implements IWindowService {
@@ -60,8 +61,8 @@ export class WindowService implements IWindowService {
return this.windowsService.pickWorkspaceAndOpen(options);
}
reloadWindow(): TPromise<void> {
return this.windowsService.reloadWindow(this.windowId);
reloadWindow(args?: ParsedArgs): TPromise<void> {
return this.windowsService.reloadWindow(this.windowId, args);
}
openDevTools(): TPromise<void> {
@@ -12,7 +12,7 @@ import { assign } from 'vs/base/common/objects';
import URI from 'vs/base/common/uri';
import product from 'vs/platform/node/product';
import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult } from 'vs/platform/windows/common/windows';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { shell, crashReporter, app, Menu, clipboard } from 'electron';
import Event, { chain, fromNodeEventEmitter } from 'vs/base/common/event';
import { IURLService } from 'vs/platform/url/common/url';
@@ -107,12 +107,12 @@ export class WindowsService implements IWindowsService, IDisposable {
return this.windowsMainService.showOpenDialog(options, codeWindow);
}
reloadWindow(windowId: number): TPromise<void> {
reloadWindow(windowId: number, args: ParsedArgs): TPromise<void> {
this.logService.trace('windowsService#reloadWindow', windowId);
const codeWindow = this.windowsMainService.getWindowById(windowId);
if (codeWindow) {
this.windowsMainService.reload(codeWindow);
this.windowsMainService.reload(codeWindow, args);
}
return TPromise.as(null);
+84 -1
View File
@@ -7,6 +7,63 @@
declare module 'vscode' {
export class FoldingRangeList {
/**
* The folding ranges.
*/
ranges: FoldingRange[];
/**
* Creates mew folding range list.
*
* @param ranges The folding ranges
*/
constructor(ranges: FoldingRange[]);
}
export class FoldingRange {
/**
* The start line number (0-based)
*/
startLine: number;
/**
* The end line number (0-based)
*/
endLine: number;
/**
* The actual color value for this color range.
*/
type?: FoldingRangeType | string;
/**
* Creates a new folding range.
*
* @param startLineNumber The first line of the fold
* @param type The last line of the fold
*/
constructor(startLineNumber: number, endLineNumber: number, type?: FoldingRangeType);
}
export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}
// export enum FileErrorCodes {
// /**
// * Not owner.
@@ -340,10 +397,27 @@ declare module 'vscode' {
}
export namespace languages {
/**
* Register a folding provider.
*
* Multiple folding can be registered for a language. In that case providers are sorted
* by their [score](#languages.match) and the best-matching provider is used. Failure
* of the selected provider will cause a failure of the whole operation.
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider A folding provider.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerFoldingProvider(selector: DocumentSelector, provider: FoldingProvider): Disposable;
export interface RenameProvider2 extends RenameProvider {
resolveInitialRenameValue?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<RenameInitialValue>;
}
}
export interface FoldingProvider {
provideFoldingRanges(document: TextDocument, token: CancellationToken): ProviderResult<FoldingRangeList>;
}
/**
* Represents the validation type of the Source Control input.
@@ -396,7 +470,7 @@ declare module 'vscode' {
*/
export interface WebviewOptions {
/**
* Should scripts be enabled in the webview contetn?
* Should scripts be enabled in the webview content?
*
* Defaults to false (scripts-disabled).
*/
@@ -423,6 +497,15 @@ declare module 'vscode' {
* webview content cannot be quickly saved and restored.
*/
readonly keepAlive?: boolean;
/**
* Root paths from which the webview can load local (filesystem) resources using the `vscode-workspace-resource:` scheme.
*
* Default to the root folders of the current workspace.
*
* Pass in an empty array to disallow access to any local resources.
*/
readonly localResourceRoots?: Uri[];
}
/**
@@ -346,6 +346,17 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
});
}
// --- folding
$registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void {
const proxy = this._proxy;
this._registrations[handle] = modes.FoldingProviderRegistry.register(toLanguageSelector(selector), <modes.FoldingProvider>{
provideFoldingRanges: (model, token) => {
return wireCancellationToken(token, proxy.$provideFoldingRanges(handle, model.uri));
}
});
}
// --- configuration
private static _reviveRegExp(regExp: ISerializedRegExp): RegExp {
@@ -151,14 +151,8 @@ class WebviewEditor extends BaseWebviewEditor {
public layout(dimension: Dimension): void {
if (this._webview) {
this.doUpdateContainer();
this._webview.layout();
}
}
public focus() {
if (this._webview) {
this._webview.focus();
}
super.layout(dimension);
}
public dispose(): void {
@@ -207,10 +201,11 @@ class WebviewEditor extends BaseWebviewEditor {
this.webview.options = {
allowScripts: input.options.enableScripts,
enableWrappedPostMessage: true
enableWrappedPostMessage: true,
useSameOriginForRoot: false,
localResourceRoots: (input && input.options.localResourceRoots) || this._contextService.getWorkspace().folders.map(x => x.uri)
};
this.webview.contents = input.html;
this.webview.style(this.themeService.getTheme());
}
private get webview(): Webview {
@@ -220,26 +215,18 @@ class WebviewEditor extends BaseWebviewEditor {
this._webview = new Webview(
this.webviewContent,
this._partService.getContainer(Parts.EDITOR_PART),
this.themeService,
this._environmentService,
this._contextService,
this._contextViewService,
this.contextKey,
this.findInputFocusContextKey,
{
enableWrappedPostMessage: true
},
false);
this.webview.style(this.themeService.getTheme());
enableWrappedPostMessage: true,
useSameOriginForRoot: false
});
this._webview.onDidClickLink(this.onDidClickLink, this, this._contentDisposables);
this.themeService.onThemeChange(theme => {
if (this._webview) {
this._webview.style(theme);
}
}, null, this._contentDisposables);
this._webview.onMessage(message => {
if (this.input) {
(this.input as WebviewInput).events.onMessage(message);
@@ -292,6 +292,9 @@ export function createApiFactory(
registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
return extHostLanguageFeatures.registerColorProvider(selector, provider);
},
registerFoldingProvider: proposedApiFunction(extension, (selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable => {
return extHostLanguageFeatures.registerFoldingProvider(selector, provider);
}),
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
return extHostLanguageFeatures.setLanguageConfiguration(language, configuration);
}
@@ -634,7 +637,10 @@ export function createApiFactory(
RelativePattern: extHostTypes.RelativePattern,
FileChangeType: extHostTypes.FileChangeType,
FileType: extHostTypes.FileType
FileType: extHostTypes.FileType,
FoldingRangeList: extHostTypes.FoldingRangeList,
FoldingRange: extHostTypes.FoldingRange,
FoldingRangeType: extHostTypes.FoldingRangeType
};
};
}
@@ -282,6 +282,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): void;
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ISerializedLanguageConfiguration): void;
}
@@ -692,6 +693,7 @@ export interface ExtHostLanguageFeaturesShape {
$resolveDocumentLink(handle: number, link: modes.ILink): TPromise<modes.ILink>;
$provideDocumentColors(handle: number, resource: UriComponents): TPromise<IRawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo): TPromise<modes.IColorPresentation[]>;
$provideFoldingRanges(handle: number, resource: UriComponents): TPromise<modes.IFoldingRangeList>;
}
export interface ExtHostQuickOpenShape {
@@ -804,10 +804,29 @@ class ColorProviderAdapter {
}
}
class FoldingProviderAdapter {
constructor(
private _documents: ExtHostDocuments,
private _provider: vscode.FoldingProvider
) { }
provideFoldingRanges(resource: URI): TPromise<modes.IFoldingRangeList> {
const doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideFoldingRanges(doc, token)).then(list => {
if (!Array.isArray(list.ranges)) {
return void 0;
}
return TypeConverters.FoldingRangeList.from(list);
});
}
}
type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter;
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
| ColorProviderAdapter | FoldingProviderAdapter;
export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
@@ -1108,6 +1127,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo));
}
registerFoldingProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable {
const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider));
this._proxy.$registerFoldingProvider(handle, selector);
return this._createDisposable(handle);
}
$provideFoldingRanges(handle: number, resource: UriComponents): TPromise<modes.IFoldingRangeList> {
return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource)));
}
// --- configuration
private static _serializeRegExp(regExp: RegExp): ISerializedRegExp {
@@ -586,6 +586,14 @@ export namespace ProgressLocation {
}
}
export namespace FoldingRangeList {
export function from(rangeList: vscode.FoldingRangeList): modes.IFoldingRangeList {
return {
ranges: rangeList.ranges.map(r => ({ startLineNumber: r.startLine + 1, endLineNumber: r.endLine + 1 }))
};
}
}
export function toTextEditorOptions(options?: vscode.TextDocumentShowOptions): ITextEditorOptions {
if (options) {
return {
+43
View File
@@ -1659,3 +1659,46 @@ export enum FileType {
}
//#endregion
//#region folding api
export class FoldingRangeList {
ranges: FoldingRange[];
constructor(ranges: FoldingRange[]) {
this.ranges = ranges;
}
}
export class FoldingRange {
startLine: number;
endLine: number;
type?: FoldingRangeType;
constructor(startLine: number, endLine: number, type?: FoldingRangeType) {
this.startLine = startLine;
this.endLine = endLine;
this.type = type;
}
}
export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}
//#endregion
@@ -11,6 +11,11 @@
flex: 1;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo.centered > .container > .title .title-label {
flex-direction: row;
justify-content: center;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .title-label a,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab .tab-label a {
text-decoration: none;
@@ -104,4 +109,4 @@
.vs-dark .monaco-workbench .show-group-editors-action,
.hc-black .monaco-workbench .show-group-editors-action {
background: url('stackview-inverse.svg') center center no-repeat;
}
}
@@ -7,14 +7,13 @@
position: absolute;
z-index: 1000;
right: 8px;
bottom: 30px; /* above status bar */
bottom: 31px;
display: none;
overflow: hidden;
}
.monaco-workbench.nostatusbar > .notifications-center {
right: 12px;
bottom: 12px;
bottom: 8px;
}
.monaco-workbench > .notifications-center.visible {
@@ -14,6 +14,16 @@
box-sizing: border-box;
}
.monaco-workbench .notifications-list-container .notification-offset-helper {
opacity: 0;
margin-top: 10px;
margin-bottom: 10px;
margin-left: 35px; /* 5px padding and 30px severity icon */
margin-right: 77px; /* 5px padding and (3 * 24px) toolbar */
position: absolute;
line-height: 22px;
}
/** Notification: Main Row */
.monaco-workbench .notifications-list-container .notification-list-item > .notification-list-item-main-row {
@@ -7,14 +7,13 @@
position: absolute;
z-index: 1000;
right: 3px;
bottom: 28px; /* above status bar */
bottom: 26px;
display: none;
overflow: hidden;
}
.monaco-workbench.nostatusbar > .notifications-toasts {
right: 12px;
bottom: 12px;
bottom: 3px;
}
.monaco-workbench > .notifications-toasts.visible {
@@ -23,7 +23,7 @@ import { localize } from 'vs/nls';
export class NotificationsCenter extends Themable {
private static MAX_DIMENSIONS = new Dimension(600, 400);
private static MAX_DIMENSIONS = new Dimension(450, 400);
private notificationsCenterContainer: HTMLElement;
private notificationsList: NotificationsList;
@@ -75,7 +75,10 @@ export class NotificationsCenter extends Themable {
this.notificationsCenterContainer = document.createElement('div');
addClass(this.notificationsCenterContainer, 'notifications-center');
this.notificationsList = this.instantiationService.createInstance(NotificationsList, this.notificationsCenterContainer, { ariaLabel: localize('notificationsList', "Notifications List") });
this.notificationsList = this.instantiationService.createInstance(NotificationsList, this.notificationsCenterContainer, {
ariaLabel: localize('notificationsList', "Notifications List"),
useShadows: false
});
this.container.appendChild(this.notificationsCenterContainer);
}
@@ -166,7 +169,7 @@ export class NotificationsCenter extends Themable {
protected updateStyles(): void {
if (this.notificationsCenterContainer) {
const widgetShadowColor = this.getColor(widgetShadow);
this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null;
this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
}
}
@@ -219,6 +222,6 @@ export class NotificationsCenter extends Themable {
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const notificationBorderColor = theme.getColor(NOTIFICATIONS_BORDER);
if (notificationBorderColor) {
collector.addRule(`.monaco-workbench > .notifications-center .notifications-list-container .notification-list-item { border-bottom: 1px solid ${notificationBorderColor}; }`);
collector.addRule(`.monaco-workbench > .notifications-center .notifications-list-container .monaco-list-row[data-last-element="false"] > .notification-list-item { border-bottom: 1px solid ${notificationBorderColor}; }`);
}
});
@@ -18,10 +18,6 @@ import { NotificationsListDelegate, NotificationRenderer } from 'vs/workbench/br
import { NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsActions';
import { NotificationFocusedContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
export interface INotificationsListOptions {
ariaLabel: string;
}
export class NotificationsList extends Themable {
private listContainer: HTMLElement;
private list: WorkbenchList<INotificationViewItem>;
@@ -30,7 +26,7 @@ export class NotificationsList extends Themable {
constructor(
private container: HTMLElement,
private options: INotificationsListOptions,
private options: IListOptions<INotificationViewItem>,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService
) {
@@ -77,9 +73,7 @@ export class NotificationsList extends Themable {
this.listContainer,
new NotificationsListDelegate(this.listContainer),
[renderer],
{
ariaLabel: this.options.ariaLabel
} as IListOptions<INotificationViewItem>
this.options
);
this.toUnbind.push(this.list);
@@ -72,7 +72,8 @@ export class NotificationsStatus {
this.statusItem = this.statusbarService.addEntry({
text: this.counter === 0 ? '$(megaphone)' : `$(megaphone) ${this.counter}`,
command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER_COMMAND_ID : this.model.notifications.length > 0 ? SHOW_NOTIFICATIONS_CENTER_COMMAND_ID : void 0,
tooltip: this.getTooltip()
tooltip: this.getTooltip(),
showBeak: this.isNotificationsCenterVisible
}, StatusbarAlignment.RIGHT, -1000 /* towards the far end of the right hand side */);
}
@@ -81,14 +82,14 @@ export class NotificationsStatus {
return localize('hideNotifications', "Hide Notifications");
}
if (this.counter === 0) {
return localize('noNotifications', "No New Notifications");
}
if (this.model.notifications.length === 0) {
return localize('zeroNotifications', "No Notifications");
}
if (this.counter === 0) {
return localize('noNotifications', "No New Notifications");
}
if (this.counter === 1) {
return localize('oneNotification', "1 new notification");
}
@@ -22,6 +22,7 @@ import { NotificationsToastsVisibleContext } from 'vs/workbench/browser/parts/no
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { localize } from 'vs/nls';
import { Severity } from 'vs/platform/notification/common/notification';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
interface INotificationToast {
list: NotificationsList;
@@ -106,7 +107,10 @@ export class NotificationsToasts extends Themable {
itemDisposeables.push(toDisposable(() => this.notificationsToastsContainer.removeChild(notificationToastContainer)));
// Create toast with item and show
const notificationList = this.instantiationService.createInstance(NotificationsList, notificationToastContainer, { ariaLabel: localize('notificationsToast', "Notification Toast") });
const notificationList = this.instantiationService.createInstance(NotificationsList, notificationToastContainer, {
ariaLabel: localize('notificationsToast', "Notification Toast"),
verticalScrollMode: ScrollbarVisibility.Hidden
});
itemDisposeables.push(notificationList);
this.mapNotificationToToast.set(item, { list: notificationList, container: notificationToastContainer, disposeables: itemDisposeables });
@@ -299,7 +303,7 @@ export class NotificationsToasts extends Themable {
protected updateStyles(): void {
this.mapNotificationToToast.forEach(toast => {
const widgetShadowColor = this.getColor(widgetShadow);
toast.container.style.boxShadow = widgetShadowColor ? `0 2px 8px ${widgetShadowColor}` : null;
toast.container.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
});
}
@@ -42,11 +42,7 @@ export class NotificationsListDelegate implements IDelegate<INotificationViewIte
private createOffsetHelper(container: HTMLElement): HTMLElement {
const offsetHelper = document.createElement('div');
offsetHelper.style.opacity = '0';
offsetHelper.style.position = 'absolute'; // do not mess with the visual layout
offsetHelper.style.width = '100%'; // ensure to fill contauner to measure true width
offsetHelper.style.overflow = 'hidden'; // do not overflow
offsetHelper.style.whiteSpace = 'nowrap'; // do not wrap to measure true width
addClass(offsetHelper, 'notification-offset-helper');
container.appendChild(offsetHelper);
@@ -63,7 +59,7 @@ export class NotificationsListDelegate implements IDelegate<INotificationViewIte
}
// Dynamic height: if message overflows
const preferredMessageHeight = this.computePreferredRows(notification) * NotificationsListDelegate.LINE_HEIGHT;
const preferredMessageHeight = this.computePreferredHeight(notification);
const messageOverflows = NotificationsListDelegate.LINE_HEIGHT < preferredMessageHeight;
if (messageOverflows) {
const overflow = preferredMessageHeight - NotificationsListDelegate.LINE_HEIGHT;
@@ -75,26 +71,28 @@ export class NotificationsListDelegate implements IDelegate<INotificationViewIte
expandedHeight += NotificationsListDelegate.ROW_HEIGHT;
}
// If the expanded height is same as collapsed, unset the expanded state
// but skip events because there is no change that has visual impact
if (expandedHeight === NotificationsListDelegate.ROW_HEIGHT) {
notification.collapse(true /* skip events, no change in height */);
}
return expandedHeight;
}
private computePreferredRows(notification: INotificationViewItem): number {
private computePreferredHeight(notification: INotificationViewItem): number {
// Render message markdown into offset helper
const renderedMessage = NotificationMessageMarkdownRenderer.render(notification.message);
this.offsetHelper.appendChild(renderedMessage);
// Compute message width taking overflow into account
const messageWidth = Math.max(renderedMessage.scrollWidth, renderedMessage.offsetWidth);
// One row per exceeding the total width of the container
const availableWidth = this.offsetHelper.offsetWidth - (20 /* paddings */ + 22 /* severity */ + (24 * 3) /* toolbar */);
const preferredRows = Math.ceil(messageWidth / availableWidth);
// Compute height
const preferredHeight = Math.max(this.offsetHelper.offsetHeight, this.offsetHelper.scrollHeight);
// Always clear offset helper after use
clearNode(this.offsetHelper);
return preferredRows;
return preferredHeight;
}
public getTemplateId(element: INotificationViewItem): string {
@@ -7,12 +7,11 @@ import 'vs/css!./media/sidebarpart';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import { Registry } from 'vs/platform/registry/common/platform';
import { Action, IAction } from 'vs/base/common/actions';
import { Action } from 'vs/base/common/actions';
import { CompositePart } from 'vs/workbench/browser/parts/compositePart';
import { Viewlet, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
import { IViewlet } from 'vs/workbench/common/viewlet';
@@ -26,7 +25,6 @@ import Event from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme';
import { ToggleSidebarVisibilityAction } from 'vs/workbench/browser/actions/toggleSidebarVisibility';
import { Dimension } from 'vs/base/browser/builder';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -134,25 +132,6 @@ export class SidebarPart extends CompositePart<Viewlet> {
return super.layout(dimension);
}
protected getTitleAreaContextMenuActions(): IAction[] {
const contextMenuActions = super.getTitleAreaContextMenuActions();
if (contextMenuActions.length) {
contextMenuActions.push(new Separator());
}
contextMenuActions.push(this.createHideSideBarAction());
return contextMenuActions;
}
private createHideSideBarAction(): IAction {
return <IAction>{
id: ToggleSidebarVisibilityAction.ID,
label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
enabled: true,
run: () => this.partService.setSideBarHidden(true)
};
}
}
class FocusSideBarAction extends Action {
@@ -9,7 +9,6 @@
height: 22px;
font-size: 12px;
padding: 0 10px;
overflow: hidden;
}
.monaco-workbench > .part.statusbar > .statusbar-item {
@@ -19,6 +18,21 @@
vertical-align: top;
}
.monaco-workbench > .part.statusbar > .statusbar-item.has-beak {
position: relative;
}
.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before {
content: '';
position: absolute;
left: 11px;
top: -5px;
border-bottom-width: 5px;
border-bottom-style: solid;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
.monaco-workbench > .part.statusbar > .statusbar-item.left > :first-child {
margin-right: 5px;
}
@@ -29,7 +29,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { isThemeColor } from 'vs/editor/common/editorCommon';
import { Color } from 'vs/base/common/color';
import { addClass, EventHelper } from 'vs/base/browser/dom';
import { addClass, EventHelper, createStyleSheet } from 'vs/base/browser/dom';
import { INotificationService } from 'vs/platform/notification/common/notification';
export class StatusbarPart extends Part implements IStatusbarService {
@@ -42,6 +42,8 @@ export class StatusbarPart extends Part implements IStatusbarService {
private statusItemsContainer: Builder;
private statusMsgDispose: IDisposable;
private styleElement: HTMLStyleElement;
constructor(
id: string,
@IInstantiationService private instantiationService: IInstantiationService,
@@ -60,7 +62,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
public addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority: number = 0): IDisposable {
// Render entry in status bar
const el = this.doCreateStatusItem(alignment, priority);
const el = this.doCreateStatusItem(alignment, priority, entry.showBeak ? 'has-beak' : void 0);
const item = this.instantiationService.createInstance(StatusBarEntryItem, entry);
const toDispose = item.render(el);
@@ -140,18 +142,31 @@ export class StatusbarPart extends Part implements IStatusbarService {
const container = this.getContainer();
// Background colors
const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND);
container.style('background-color', backgroundColor);
container.style('color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND));
container.style('background-color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND));
// Border color
const borderColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BORDER : STATUS_BAR_NO_FOLDER_BORDER) || this.getColor(contrastBorder);
container.style('border-top-width', borderColor ? '1px' : null);
container.style('border-top-style', borderColor ? 'solid' : null);
container.style('border-top-color', borderColor);
// Notification Beak
if (!this.styleElement) {
this.styleElement = createStyleSheet(container.getHTMLElement());
}
this.styleElement.innerHTML = `.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`;
}
private doCreateStatusItem(alignment: StatusbarAlignment, priority: number = 0): HTMLElement {
private doCreateStatusItem(alignment: StatusbarAlignment, priority: number = 0, extraClass?: string): HTMLElement {
const el = document.createElement('div');
addClass(el, 'statusbar-item');
if (extraClass) {
addClass(el, extraClass);
}
if (alignment === StatusbarAlignment.RIGHT) {
addClass(el, 'right');
@@ -11,7 +11,7 @@ import { ColorIdentifier, contrastBorder } from 'vs/platform/theme/common/colorR
import { attachStyler, IColorMapping, IThemable } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND } from 'vs/workbench/common/theme';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { append, $, trackFocus, toggleClass } from 'vs/base/browser/dom';
import { append, $, trackFocus, toggleClass, EventType, isAncestor } from 'vs/base/browser/dom';
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { firstIndex } from 'vs/base/common/arrays';
import { IAction, IActionRunner } from 'vs/base/common/actions';
@@ -26,6 +26,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { PanelView, IPanelViewOptions, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
export interface IPanelColors extends IColorMapping {
dropBackground?: ColorIdentifier;
@@ -165,10 +167,12 @@ export class PanelViewlet extends Viewlet {
constructor(
id: string,
private options: IViewsViewletOptions,
@IPartService partService: IPartService,
@IContextMenuService protected contextMenuService: IContextMenuService,
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService
) {
super(id, telemetryService, themeService);
super(id, partService, telemetryService, themeService);
}
async create(parent: Builder): TPromise<void> {
@@ -176,7 +180,26 @@ export class PanelViewlet extends Viewlet {
const container = parent.getHTMLElement();
this.panelview = this._register(new PanelView(container, this.options));
this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel));
this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel)));
this._register(parent.on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
}
private showContextMenu(event: StandardMouseEvent): void {
event.stopPropagation();
event.preventDefault();
for (const panelItem of this.panelItems) {
// Do not show context menu if requested from inside panel views
if (isAncestor(event.target, panelItem.panel.element)) {
return;
}
}
let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(this.getContextMenuActions())
});
}
getTitle(): string {
@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import * as DOM from 'vs/base/browser/dom';
@@ -11,7 +10,7 @@ import { $, Dimension, Builder } from 'vs/base/browser/builder';
import { Scope } from 'vs/workbench/common/memento';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { firstIndex } from 'vs/base/common/arrays';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -31,6 +30,8 @@ import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/th
import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
import Event, { Emitter } from 'vs/base/common/event';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { localize } from 'vs/nls';
export interface IViewOptions extends IPanelOptions {
id: string;
@@ -200,6 +201,7 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
id: string,
private location: ViewLocation,
private showHeaderInTitleWhenSingleView: boolean,
@IPartService partService: IPartService,
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService protected storageService: IStorageService,
@IInstantiationService protected instantiationService: IInstantiationService,
@@ -208,7 +210,7 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
@IContextMenuService protected contextMenuService: IContextMenuService,
@IExtensionService protected extensionService: IExtensionService
) {
super(id, { showHeaderInTitleWhenSingleView, dnd: true }, telemetryService, themeService);
super(id, { showHeaderInTitleWhenSingleView, dnd: true }, partService, contextMenuService, telemetryService, themeService);
this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE);
}
@@ -232,7 +234,8 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
}
getContextMenuActions(): IAction[] {
return this.getViewDescriptorsFromRegistry(true)
const result: IAction[] = [];
const viewToggleActions = this.getViewDescriptorsFromRegistry(true)
.filter(viewDescriptor => viewDescriptor.canToggleVisibility && this.contextKeyService.contextMatchesRules(viewDescriptor.when))
.map(viewDescriptor => (<IAction>{
id: `${viewDescriptor.id}.toggleVisibility`,
@@ -241,6 +244,13 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
enabled: true,
run: () => this.toggleViewVisibility(viewDescriptor.id)
}));
result.push(...viewToggleActions);
const parentActions = super.getContextMenuActions();
if (viewToggleActions.length && parentActions.length) {
result.push(new Separator());
}
result.push(...parentActions);
return result;
}
setVisible(visible: boolean): TPromise<void> {
@@ -481,9 +491,7 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
this.viewHeaderContextMenuListeners.push(DOM.addDisposableListener(view.draggableElement, DOM.EventType.CONTEXT_MENU, e => {
e.stopPropagation();
e.preventDefault();
if (viewDescriptor.canToggleVisibility) {
this.onContextMenu(new StandardMouseEvent(e), view);
}
this.onContextMenu(new StandardMouseEvent(e), viewDescriptor);
}));
}
}
@@ -498,19 +506,26 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
}
}
private onContextMenu(event: StandardMouseEvent, view: ViewsViewletPanel): void {
private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void {
event.stopPropagation();
event.preventDefault();
const actions: IAction[] = [];
actions.push(<IAction>{
id: `${viewDescriptor.id}.removeView`,
label: localize('hideView', "Hide"),
enabled: viewDescriptor.canToggleVisibility,
run: () => this.toggleViewVisibility(viewDescriptor.id)
});
const otherActions = this.getContextMenuActions();
if (otherActions.length) {
actions.push(...[new Separator(), ...otherActions]);
}
let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as([<IAction>{
id: `${view.id}.removeView`,
label: nls.localize('hideView', "Hide"),
enabled: true,
run: () => this.toggleViewVisibility(view.id)
}]),
getActions: () => TPromise.as(actions)
});
}
@@ -610,6 +625,7 @@ export class PersistentViewsViewlet extends ViewsViewlet {
location: ViewLocation,
private readonly viewletStateStorageId: string,
showHeaderInTitleWhenSingleView: boolean,
@IPartService partService: IPartService,
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService,
@IInstantiationService instantiationService: IInstantiationService,
@@ -619,7 +635,7 @@ export class PersistentViewsViewlet extends ViewsViewlet {
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService
) {
super(id, location, showHeaderInTitleWhenSingleView, telemetryService, storageService, instantiationService, themeService, contextKeyService, contextMenuService, extensionService);
super(id, location, showHeaderInTitleWhenSingleView, partService, telemetryService, storageService, instantiationService, themeService, contextKeyService, contextMenuService, extensionService);
this.hiddenViewsStorageId = `${this.viewletStateStorageId}.hidden`;
this._register(this.onDidChangeViewVisibilityState(id => this.onViewVisibilityChanged(id)));
}
+22 -1
View File
@@ -7,19 +7,40 @@ import nls = require('vs/nls');
import { TPromise } from 'vs/base/common/winjs.base';
import DOM = require('vs/base/browser/dom');
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { Action, IAction } from 'vs/base/common/actions';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite';
import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
import { ToggleSidebarVisibilityAction } from 'vs/workbench/browser/actions/toggleSidebarVisibility';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export abstract class Viewlet extends Composite implements IViewlet {
constructor(id: string,
private partService: IPartService,
telemetryService: ITelemetryService,
themeService: IThemeService
) {
super(id, telemetryService, themeService);
}
public getOptimalWidth(): number {
return null;
}
public getContextMenuActions(): IAction[] {
return [<IAction>{
id: ToggleSidebarVisibilityAction.ID,
label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
enabled: true,
run: () => this.partService.setSideBarHidden(true)
}];
}
}
/**
+6 -3
View File
@@ -197,7 +197,7 @@ export interface INotificationViewItem {
readonly onDidLabelChange: Event<INotificationViewItemLabelChangeEvent>;
expand(): void;
collapse(): void;
collapse(skipEvents?: boolean): void;
toggle(): void;
hasProgress(): boolean;
@@ -494,13 +494,16 @@ export class NotificationViewItem implements INotificationViewItem {
this._onDidExpansionChange.fire();
}
public collapse(): void {
public collapse(skipEvents?: boolean): void {
if (!this._expanded || !this.canCollapse) {
return;
}
this._expanded = false;
this._onDidExpansionChange.fire();
if (!skipEvents) {
this._onDidExpansionChange.fire();
}
}
public toggle(): void {
@@ -555,6 +555,24 @@ export class ReloadWindowAction extends Action {
}
}
export class ReloadWindowWithExtensionsDisabledAction extends Action {
static readonly ID = 'workbench.action.reloadWindowWithExtensionsDisabled';
static LABEL = nls.localize('reloadWindowWithExntesionsDisabled', "Reload Window With Extensions Disabled");
constructor(
id: string,
label: string,
@IWindowService private windowService: IWindowService
) {
super(id, label);
}
run(): TPromise<boolean> {
return this.windowService.reloadWindow({ _: [], 'disable-extensions': true }).then(() => true);
}
}
export abstract class BaseSwitchWindow extends Action {
private closeWindowAction: CloseWindowAction;
@@ -84,7 +84,7 @@ import { MenuService } from 'vs/platform/actions/common/menuService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar } from 'vs/workbench/electron-browser/actions';
import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService';
@@ -207,7 +207,7 @@ export class Workbench implements IPartService {
private sideBarPosition: Position;
private panelPosition: Position;
private panelHidden: boolean;
private editorBackgroundDelayer: Delayer<void>;
private editorBackgroundDelayer: Delayer<any>;
private closeEmptyWindowScheduler: RunOnceScheduler;
private editorsVisibleContext: IContextKey<boolean>;
private inZenMode: IContextKey<boolean>;
@@ -418,7 +418,7 @@ export class Workbench implements IPartService {
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyCode.KEY_R } : void 0), 'Reload Window');
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_I } } : void 0), 'Developer: Toggle Developer Tools', localize('developer', "Developer"));
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: isDeveloping ? null : KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', localize('file', "File"));
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Reload Window Without Extensions');
// Actions for macOS native tabs management (only when enabled)
const windowConfig = this.configurationService.getValue<IWindowConfiguration>();
if (windowConfig && windowConfig.window && windowConfig.window.nativeTabs) {
@@ -26,6 +26,7 @@ import { ViewLocation } from 'vs/workbench/common/views';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IPartService } from 'vs/workbench/services/part/common/partService';
export class DebugViewlet extends PersistentViewsViewlet {
@@ -35,6 +36,7 @@ export class DebugViewlet extends PersistentViewsViewlet {
private panelListeners = new Map<string, IDisposable>();
constructor(
@IPartService partService: IPartService,
@ITelemetryService telemetryService: ITelemetryService,
@IProgressService private progressService: IProgressService,
@IDebugService private debugService: IDebugService,
@@ -46,7 +48,7 @@ export class DebugViewlet extends PersistentViewsViewlet {
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService
) {
super(VIEWLET_ID, ViewLocation.Debug, `${VIEWLET_ID}.state`, false, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
super(VIEWLET_ID, ViewLocation.Debug, `${VIEWLET_ID}.state`, false, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
this.progressRunner = null;
@@ -11,7 +11,7 @@ import { IPartService, Parts } from 'vs/workbench/services/part/common/partServi
import { IDebugService, State } from 'vs/workbench/parts/debug/common/debug';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_BACKGROUND, Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_BORDER } from 'vs/workbench/common/theme';
import { addClass, removeClass } from 'vs/base/browser/dom';
import { addClass, removeClass, createStyleSheet } from 'vs/base/browser/dom';
// colors for theming
@@ -34,6 +34,7 @@ export const STATUS_BAR_DEBUGGING_BORDER = registerColor('statusBar.debuggingBor
}, localize('statusBarDebuggingBorder', "Status bar border color separating to the sidebar and editor when a program is being debugged. The status bar is shown in the bottom of the window"));
export class StatusBarColorProvider extends Themable implements IWorkbenchContribution {
private styleElement: HTMLStyleElement;
constructor(
@IThemeService themeService: IThemeService,
@@ -61,13 +62,23 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri
removeClass(container, 'debugging');
}
container.style.backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND));
// Container Colors
const backgroundColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_DEBUGGING_BACKGROUND, STATUS_BAR_BACKGROUND));
container.style.backgroundColor = backgroundColor;
container.style.color = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_DEBUGGING_FOREGROUND, STATUS_BAR_FOREGROUND));
// Border Color
const borderColor = this.getColor(this.getColorKey(STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_DEBUGGING_BORDER, STATUS_BAR_BORDER)) || this.getColor(contrastBorder);
container.style.borderTopWidth = borderColor ? '1px' : null;
container.style.borderTopStyle = borderColor ? 'solid' : null;
container.style.borderTopColor = borderColor;
// Notification Beak
if (!this.styleElement) {
this.styleElement = createStyleSheet(container);
}
this.styleElement.innerHTML = `.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor} !important; }`;
}
private getColorKey(noFolderColor: string, debuggingColor: string, normalColor: string): string {
@@ -9,7 +9,7 @@ import { IAction } from 'vs/base/common/actions';
import * as lifecycle from 'vs/base/common/lifecycle';
import * as errors from 'vs/base/common/errors';
import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import * as dom from 'vs/base/browser/dom';
import severity from 'vs/base/common/severity';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
@@ -23,6 +23,7 @@ import { CopyAction, CopyAllAction } from 'vs/workbench/parts/debug/electron-bro
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector';
import { CollapseAction } from 'vs/workbench/browser/viewlet';
const $ = dom.$;
@@ -437,6 +438,8 @@ export class ReplExpressionsActionProvider implements IActionProvider {
const actions: IAction[] = [];
actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL));
actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree));
actions.push(new CollapseAction(tree, true, 'explore-action collapse-all'));
actions.push(new Separator());
actions.push(this.instantiationService.createInstance(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL));
return TPromise.as(actions);
@@ -56,12 +56,17 @@ export class TerminalSupport {
const result = cp.spawnSync('wmic', ['process', 'get', 'ParentProcessId']);
if (result.stdout) {
const pids = result.stdout.toString().split('\r\n');
return pids.some(p => parseInt(p) === t.processId);
if (!pids.some(p => parseInt(p) === t.processId)) {
return false;
}
}
} else {
const result = cp.spawnSync('/usr/bin/pgrep', ['-P', String(t.processId)]);
const result = cp.spawnSync('/usr/bin/pgrep', ['-lP', String(t.processId)]);
if (result.stdout) {
return result.stdout.toString().trim().length > 0;
const r = result.stdout.toString().trim();
if (r.length === 0 || r.indexOf(' tmux') >= 0) { // ignore 'tmux'; see #43683
return false;
}
}
}
}
@@ -52,7 +52,6 @@ import { Color } from 'vs/base/common/color';
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { assign } from 'vs/base/common/objects';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { INotificationService } from 'vs/platform/notification/common/notification';
/** A context key that is set when an extension editor webview has focus. */
@@ -183,19 +182,18 @@ export class ExtensionEditor extends BaseEditor {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService private instantiationService: IInstantiationService,
@IViewletService private viewletService: IViewletService,
@IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IViewletService private readonly viewletService: IViewletService,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IThemeService protected themeService: IThemeService,
@IKeybindingService private keybindingService: IKeybindingService,
@INotificationService private notificationService: INotificationService,
@IOpenerService private openerService: IOpenerService,
@IPartService private partService: IPartService,
@IContextViewService private contextViewService: IContextViewService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IExtensionTipsService private extensionTipsService: IExtensionTipsService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IWorkspaceContextService private contextService: IWorkspaceContextService
@IKeybindingService private readonly keybindingService: IKeybindingService,
@INotificationService private readonly notificationService: INotificationService,
@IOpenerService private readonly openerService: IOpenerService,
@IPartService private readonly partService: IPartService,
@IContextViewService private readonly contextViewService: IContextViewService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService,
@IEnvironmentService private readonly environmentService: IEnvironmentService
) {
super(ExtensionEditor.ID, telemetryService, themeService);
@@ -421,11 +419,9 @@ export class ExtensionEditor extends BaseEditor {
.then<void>(body => {
const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders;
const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : {};
this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.environmentService, this.contextService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions, false);
this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions);
const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview);
this.contentDisposables.push(toDisposable(removeLayoutParticipant));
this.activeWebview.style(this.themeService.getTheme());
this.activeWebview.contents = body;
this.activeWebview.onDidClickLink(link => {
@@ -434,7 +430,6 @@ export class ExtensionEditor extends BaseEditor {
this.openerService.open(link);
}
}, null, this.contentDisposables);
this.themeService.onThemeChange(theme => this.activeWebview.style(theme), null, this.contentDisposables);
this.contentDisposables.push(this.activeWebview);
})
.then(null, () => {
@@ -1601,7 +1601,7 @@ export class DisableAllAction extends Action {
}
run(): TPromise<any> {
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.Disabled)));
return TPromise.join(this.extensionsWorkbenchService.local.filter(e => e.type === LocalExtensionType.User).map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.Disabled)));
}
dispose(): void {
@@ -1633,7 +1633,7 @@ export class DisableAllWorkpsaceAction extends Action {
}
run(): TPromise<any> {
return TPromise.join(this.extensionsWorkbenchService.local.map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.WorkspaceDisabled)));
return TPromise.join(this.extensionsWorkbenchService.local.filter(e => e.type === LocalExtensionType.User).map(e => this.extensionsWorkbenchService.setEnablement(e, EnablementState.WorkspaceDisabled)));
}
dispose(): void {
@@ -1645,7 +1645,7 @@ export class DisableAllWorkpsaceAction extends Action {
export class EnableAllAction extends Action {
static readonly ID = 'workbench.extensions.action.enableAll';
static LABEL = localize('enableAll', "Enable All Installed Extensions");
static LABEL = localize('enableAll', "Enable All Extensions");
private disposables: IDisposable[] = [];
@@ -1676,7 +1676,7 @@ export class EnableAllAction extends Action {
export class EnableAllWorkpsaceAction extends Action {
static readonly ID = 'workbench.extensions.action.enableAllWorkspace';
static LABEL = localize('enableAllWorkspace', "Enable All Installed Extensions for this Workspace");
static LABEL = localize('enableAllWorkspace', "Enable All Extensions for this Workspace");
private disposables: IDisposable[] = [];
@@ -55,6 +55,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IPartService } from 'vs/workbench/services/part/common/partService';
interface SearchInputEvent extends Event {
target: HTMLInputElement;
@@ -88,6 +89,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
private disposables: IDisposable[] = [];
constructor(
@IPartService partService: IPartService,
@ITelemetryService telemetryService: ITelemetryService,
@IProgressService private progressService: IProgressService,
@IInstantiationService instantiationService: IInstantiationService,
@@ -104,7 +106,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService
) {
super(VIEWLET_ID, ViewLocation.Extensions, `${VIEWLET_ID}.state`, true, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
super(VIEWLET_ID, ViewLocation.Extensions, `${VIEWLET_ID}.state`, true, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
this.registerViews();
this.searchDelayer = new ThrottledDelayer(500);
@@ -27,10 +27,6 @@
height: calc(100% - 38px);
}
.extensions-viewlet > .extensions .extensions-list > .monaco-list {
height: auto;
}
.extensions-viewlet > .extensions .list-actionbar-container .monaco-action-bar .action-item > .octicon {
font-size: 12px;
line-height: 1;
@@ -34,6 +34,7 @@ import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/commo
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Disposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IPartService } from 'vs/workbench/services/part/common/partService';
export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution {
@@ -150,6 +151,7 @@ export class ExplorerViewlet extends PersistentViewsViewlet implements IExplorer
private viewletVisibleContextKey: IContextKey<boolean>;
constructor(
@IPartService partService: IPartService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
@IStorageService protected storageService: IStorageService,
@@ -162,7 +164,7 @@ export class ExplorerViewlet extends PersistentViewsViewlet implements IExplorer
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService
) {
super(VIEWLET_ID, ViewLocation.Explorer, ExplorerViewlet.EXPLORER_VIEWS_STATE, true, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
super(VIEWLET_ID, ViewLocation.Explorer, ExplorerViewlet.EXPLORER_VIEWS_STATE, true, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
this.viewletState = new FileViewletState();
this.viewletVisibleContextKey = ExplorerViewletVisibleContext.bindTo(contextKeyService);
@@ -799,11 +799,15 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
// In-Explorer DND
else {
const sources: FileStat[] = data.getData();
if (target instanceof Model) {
if (sources.length === 1 && sources[0].isRoot) {
return DRAG_OVER_ACCEPT_BUBBLE_DOWN(false);
}
return DRAG_OVER_REJECT;
}
const sources: FileStat[] = data.getData();
if (!Array.isArray(sources)) {
return DRAG_OVER_REJECT;
}
@@ -864,9 +868,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
// In-Explorer DND (Move/Copy file)
else {
if (target instanceof FileStat) {
promise = this.handleExplorerDrop(tree, data, target, originalEvent);
}
promise = this.handleExplorerDrop(tree, data, target, originalEvent);
}
promise.done(null, errors.onUnexpectedError);
@@ -915,7 +917,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
});
}
private handleExplorerDrop(tree: ITree, data: IDragAndDropData, target: FileStat, originalEvent: DragMouseEvent): TPromise<void> {
private handleExplorerDrop(tree: ITree, data: IDragAndDropData, target: FileStat | Model, originalEvent: DragMouseEvent): TPromise<void> {
const sources: FileStat[] = distinctParents(data.getData(), s => s.resource);
const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh);
@@ -955,19 +957,20 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
});
}
private doHandleExplorerDrop(tree: ITree, source: FileStat, target: FileStat, isCopy: boolean): TPromise<void> {
private doHandleExplorerDrop(tree: ITree, source: FileStat, target: FileStat | Model, isCopy: boolean): TPromise<void> {
return tree.expand(target).then(() => {
if (source.isRoot) {
const folders = this.contextService.getWorkspace().folders;
let sourceIndex: number;
let targetIndex: number;
const workspaceCreationData: IWorkspaceFolderCreationData[] = [];
const targetUri = target instanceof FileStat ? target.resource : folders[folders.length - 1].uri;
for (let index = 0; index < folders.length; index++) {
if (folders[index].uri.toString() === source.resource.toString()) {
sourceIndex = index;
}
if (folders[index].uri.toString() === target.resource.toString()) {
if (folders[index].uri.toString() === targetUri.toString()) {
targetIndex = index;
}
workspaceCreationData.push({
@@ -1004,6 +1007,9 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
return TPromise.join(dirtyMoved.map(d => this.backupFileService.discardResourceBackup(d)));
};
if (!(target instanceof FileStat)) {
return TPromise.as(void 0);
}
// 1. check for dirty files that are being moved and backup to new target
const dirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, source.resource, !isLinux /* ignorecase */));
@@ -16,7 +16,6 @@ import { HtmlPreviewPart } from 'vs/workbench/parts/html/browser/htmlPreviewPart
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { MenuRegistry } from 'vs/platform/actions/common/actions';
import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
@@ -31,6 +30,7 @@ function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI
}
// --- Register Editor
(<IEditorRegistry>Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor(
HtmlPreviewPart,
HtmlPreviewPart.ID,
@@ -39,17 +39,11 @@ function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI
// --- Register Commands
const defaultPreviewHtmlOptions: HtmlInputOptions = {
allowScripts: true,
allowSvgs: true
};
CommandsRegistry.registerCommand('_workbench.previewHtml', function (
accessor: ServicesAccessor,
resource: URI | string,
position?: EditorPosition,
label?: string,
options?: HtmlInputOptions
label?: string
) {
const uri = resource instanceof URI ? resource : URI.parse(resource);
label = label || uri.fsPath;
@@ -66,9 +60,13 @@ CommandsRegistry.registerCommand('_workbench.previewHtml', function (
}
}
const inputOptions = (Object as any).assign({}, options || defaultPreviewHtmlOptions);
const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);
inputOptions.svgWhiteList = extensionsWorkbenchService.allowedBadgeProviders;
const inputOptions: HtmlInputOptions = {
allowScripts: true,
allowSvgs: true,
svgWhiteList: extensionsWorkbenchService.allowedBadgeProviders
};
// Otherwise, create new input and open it
if (!input) {
@@ -93,19 +91,3 @@ CommandsRegistry.registerCommand('_workbench.htmlPreview.postMessage', function
}
return activePreviews.length > 0;
});
CommandsRegistry.registerCommand('_webview.openDevTools', function () {
const elements = document.querySelectorAll('webview.ready');
for (let i = 0; i < elements.length; i++) {
try {
(elements.item(i) as Electron.WebviewTag).openDevTools();
} catch (e) {
console.error(e);
}
}
});
MenuRegistry.addCommand({
id: '_webview.openDevTools',
title: localize('devtools.webview', "Developer: Webview Tools")
});
@@ -26,7 +26,6 @@ import { Webview, WebviewOptions } from './webview';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { WebviewEditor } from './webviewEditor';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
/**
@@ -55,8 +54,7 @@ export class HtmlPreviewPart extends WebviewEditor {
@IOpenerService private readonly openerService: IOpenerService,
@IPartService private readonly partService: IPartService,
@IContextViewService private readonly _contextViewService: IContextViewService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService
@IEnvironmentService private readonly _environmentService: IEnvironmentService
) {
super(HtmlPreviewPart.ID, telemetryService, themeService, storageService, contextKeyService);
}
@@ -91,13 +89,15 @@ export class HtmlPreviewPart extends WebviewEditor {
this._webview = new Webview(
this.content,
this.partService.getContainer(Parts.EDITOR_PART),
this.themeService,
this._environmentService,
this._contextService,
this._contextViewService,
this.contextKey,
this.findInputFocusContextKey,
webviewOptions,
true);
{
...webviewOptions,
useSameOriginForRoot: true
});
if (this.input && this.input instanceof HtmlInput) {
const state = this.loadViewState(this.input.getResource());
@@ -107,7 +107,6 @@ export class HtmlPreviewPart extends WebviewEditor {
const resourceUri = this.input.getResource();
this.webview.baseUrl = resourceUri.toString(true);
}
this.onThemeChange(this.themeService.getTheme());
this._webviewDisposables = [
this._webview,
this._webview.onDidClickLink(uri => this.openerService.open(uri)),
@@ -157,13 +156,8 @@ export class HtmlPreviewPart extends WebviewEditor {
const { width, height } = dimension;
this.content.style.width = `${width}px`;
this.content.style.height = `${height}px`;
if (this._webview) {
this._webview.layout();
}
}
public focus(): void {
this.webview.focus();
super.layout(dimension);
}
public clearInput(): void {
@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { ContextKeyExpr, } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -11,10 +12,12 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor
import { KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './webviewEditor';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { ShowWebViewEditorFindWidgetAction, ShowWebViewEditorFindTermCommand, HideWebViewEditorFindCommand } from './webviewCommands';
import { ShowWebViewEditorFindWidgetAction, ShowWebViewEditorFindTermCommand, HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, ReloadWebviewAction } from './webviewCommands';
const category = 'Webview';
const webviewDeveloperCategory = nls.localize('developer', "Developer");
const actionRegistry = <IWorkbenchActionRegistry>Registry.as(ActionExtensions.WorkbenchActions);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowWebViewEditorFindWidgetAction, ShowWebViewEditorFindWidgetAction.ID, ShowWebViewEditorFindWidgetAction.LABEL, {
@@ -52,3 +55,14 @@ const hideCommand = new HideWebViewEditorFindCommand({
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL),
'Webview Tools',
webviewDeveloperCategory);
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL),
'Reload Webview',
webviewDeveloperCategory);
+28 -35
View File
@@ -10,20 +10,21 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
import { addDisposableListener, addClass } from 'vs/base/browser/dom';
import { editorBackground, editorForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, LIGHT, DARK } from 'vs/platform/theme/common/themeService';
import { ITheme, LIGHT, DARK, IThemeService } from 'vs/platform/theme/common/themeService';
import { WebviewFindWidget } from './webviewFindWidget';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { normalize, nativeSep } from 'vs/base/common/paths';
import { startsWith } from 'vs/base/common/strings';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
export interface WebviewOptions {
readonly allowScripts?: boolean;
readonly allowSvgs?: boolean;
readonly svgWhiteList?: string[];
readonly enableWrappedPostMessage?: boolean;
readonly useSameOriginForRoot?: boolean;
readonly localResourceRoots?: URI[];
}
export class Webview {
@@ -33,17 +34,17 @@ export class Webview {
private _webviewFindWidget: WebviewFindWidget;
private _findStarted: boolean = false;
private _contents: string = '';
constructor(
private readonly parent: HTMLElement,
private readonly _styleElement: Element,
private readonly _themeService: IThemeService,
private readonly _environmentService: IEnvironmentService,
private readonly _contextService: IWorkspaceContextService,
private readonly _contextViewService: IContextViewService,
private readonly _contextKey: IContextKey<boolean>,
private readonly _findInputContextKey: IContextKey<boolean>,
private _options: WebviewOptions,
useSameOriginForRoot: boolean
private _options: WebviewOptions
) {
this._webview = document.createElement('webview');
this._webview.setAttribute('partition', this._options.allowSvgs ? 'webview' : `webview${Date.now()}`);
@@ -60,7 +61,7 @@ export class Webview {
this._webview.style.outline = '0';
this._webview.preload = require.toUrl('./webview-pre.js');
this._webview.src = useSameOriginForRoot ? require.toUrl('./webview.html') : 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E';
this._webview.src = this._options.useSameOriginForRoot ? require.toUrl('./webview.html') : 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E';
this._ready = new Promise<this>(resolve => {
const subscription = addDisposableListener(this._webview, 'ipc-message', (event) => {
@@ -74,7 +75,7 @@ export class Webview {
});
});
if (!useSameOriginForRoot) {
if (!this._options.useSameOriginForRoot) {
let loaded = false;
this._disposables.push(addDisposableListener(this._webview, 'did-start-loading', () => {
if (loaded) {
@@ -84,7 +85,6 @@ export class Webview {
const contents = this._webview.getWebContents();
this.registerFileProtocols(contents);
}));
}
@@ -167,22 +167,20 @@ export class Webview {
if (this._contextKey) {
this._contextKey.set(true);
}
this._onFocus.fire();
}),
addDisposableListener(this._webview, 'blur', () => {
if (this._contextKey) {
this._contextKey.reset();
}
this._onBlur.fire();
}),
addDisposableListener(this._webview, 'found-in-page', (event) => {
this._onFoundInPageResults.fire(event.result);
})
);
this._webviewFindWidget = new WebviewFindWidget(this._contextViewService, this);
this._disposables.push(this._webviewFindWidget);
this.style(this._themeService.getTheme());
this._themeService.onThemeChange(this.style, this, this._disposables);
if (parent) {
parent.appendChild(this._webviewFindWidget.getDomNode());
parent.appendChild(this._webview);
@@ -218,44 +216,36 @@ export class Webview {
private readonly _onDidScroll = new Emitter<{ scrollYPercentage: number }>();
public readonly onDidScroll: Event<{ scrollYPercentage: number }> = this._onDidScroll.event;
private readonly _onFoundInPageResults = new Emitter<Electron.FoundInPageResult>();
public readonly onFindResults: Event<Electron.FoundInPageResult> = this._onFoundInPageResults.event;
private readonly _onMessage = new Emitter<any>();
public readonly onMessage: Event<any> = this._onMessage.event;
private readonly _onFocus = new Emitter<void>();
public readonly onFocus: Event<void> = this._onFocus.event;
private readonly _onBlur = new Emitter<void>();
public readonly onBlur: Event<void> = this._onBlur.event;
private _send(channel: string, ...args: any[]): void {
this._ready
.then(() => this._webview.send(channel, ...args))
.catch(err => console.error(err));
}
set initialScrollProgress(value: number) {
public set initialScrollProgress(value: number) {
this._send('initial-scroll-position', value);
}
set options(value: WebviewOptions) {
public set options(value: WebviewOptions) {
this._options = value;
}
set contents(value: string) {
public set contents(value: string) {
this._contents = value;
this._send('content', {
contents: value,
options: this._options
});
}
set baseUrl(value: string) {
public set baseUrl(value: string) {
this._send('baseUrl', value);
}
focus(): void {
public focus(): void {
this._webview.focus();
this._send('focus');
}
@@ -270,7 +260,7 @@ export class Webview {
});
}
style(theme: ITheme): void {
private style(theme: ITheme): void {
const { fontFamily, fontWeight, fontSize } = window.getComputedStyle(this._styleElement); // TODO@theme avoid styleElement
const styles = {
@@ -330,16 +320,16 @@ export class Webview {
return;
}
registerFileProtocol(contents, 'vscode-core-resource', [
registerFileProtocol(contents, 'vscode-core-resource', () => [
this._environmentService.appRoot
]);
registerFileProtocol(contents, 'vscode-extension-resource', [
registerFileProtocol(contents, 'vscode-extension-resource', () => [
this._environmentService.extensionsPath,
this._environmentService.appRoot,
this._environmentService.extensionDevelopmentPath
]);
registerFileProtocol(contents, 'vscode-workspace-resource',
this._contextService.getWorkspace().folders.map(folder => folder.uri.fsPath)
registerFileProtocol(contents, 'vscode-workspace-resource', () =>
this._options.localResourceRoots.map(uri => uri.fsPath)
);
}
@@ -361,7 +351,6 @@ export class Webview {
this._findStarted = true;
this._webview.findInPage(value, findOptions);
return;
}
/**
@@ -406,6 +395,10 @@ export class Webview {
public showPreviousFindTerm() {
this._webviewFindWidget.showPreviousFindTerm();
}
public reload() {
this.contents = this._contents;
}
}
@@ -430,11 +423,11 @@ namespace ApiThemeClassName {
function registerFileProtocol(
contents: Electron.WebContents,
protocol: string,
roots: string[]
getRoots: () => string[]
) {
contents.session.protocol.registerFileProtocol(protocol, (request, callback: any) => {
const requestPath = URI.parse(request.url).fsPath;
for (const root of roots) {
for (const root of getRoots()) {
const normalizedPath = normalize(requestPath, true);
if (startsWith(normalizedPath, root + nativeSep)) {
callback({ path: normalizedPath });
@@ -82,3 +82,55 @@ export class ShowWebViewEditorFindTermCommand extends Command {
return null;
}
}
export class OpenWebviewDeveloperToolsAction extends Action {
static readonly ID = 'workbench.action.webview.openDeveloperTools';
static LABEL = nls.localize('openToolsLabel', "Webview Tools");
public constructor(
id: string,
label: string
) {
super(id, label);
}
public run(): TPromise<any> {
const elements = document.querySelectorAll('webview.ready');
for (let i = 0; i < elements.length; i++) {
try {
(elements.item(i) as Electron.WebviewTag).openDevTools();
} catch (e) {
console.error(e);
}
}
return null;
}
}
export class ReloadWebviewAction extends Action {
static readonly ID = 'workbench.action.webview.reloadWebviewAction';
static LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews");
public constructor(
id: string,
label: string,
@IWorkbenchEditorService private readonly workbenchEditorService: IWorkbenchEditorService
) {
super(id, label);
}
public run(): TPromise<any> {
for (const webview of this.getVisibleWebviews()) {
webview.reload();
}
return null;
}
private getVisibleWebviews() {
return this.workbenchEditorService.getVisibleEditors()
.filter(c => c && (c as any).isWebviewEditor)
.map(e => e as WebviewEditor);
}
}
@@ -12,6 +12,7 @@ import { IContextKey, RawContextKey, IContextKeyService } from 'vs/platform/cont
import { Webview } from './webview';
import { Builder } from 'vs/base/browser/builder';
import { Dimension } from 'vs/workbench/services/part/common/partService';
/** A context key that is set when a webview editor has focus. */
export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS = new RawContextKey<boolean>('webviewEditorFocus', false);
@@ -73,15 +74,26 @@ export abstract class WebviewEditor extends BaseWebviewEditor {
}
}
public updateStyles() {
super.updateStyles();
public get isWebviewEditor() {
return true;
}
public reload() {
if (this._webview) {
this._webview.style(this.themeService.getTheme());
this._webview.reload();
}
}
public get isWebviewEditor() {
return true;
public layout(dimension: Dimension): void {
if (this._webview) {
this._webview.layout();
}
}
public focus(): void {
if (this._webview) {
this._webview.focus();
}
}
protected abstract createEditor(parent: Builder): void;
+13 -3
View File
@@ -22,14 +22,24 @@ export const OUTPUT_MIME = 'text/x-code-output';
export const OUTPUT_SCHEME = 'output';
/**
* Output resource scheme.
* Id used by the output editor.
*/
export const OUTPUT_MODE_ID = 'Log';
/**
* Mime type used by the log output editor.
*/
export const LOG_MIME = 'text/x-code-log-output';
/**
* Log resource scheme.
*/
export const LOG_SCHEME = 'log';
/**
* Id used by the output editor.
* Id used by the log output editor.
*/
export const OUTPUT_MODE_ID = 'Log';
export const LOG_MODE_ID = 'log';
/**
* Output panel id
@@ -11,7 +11,7 @@ import { RunOnceScheduler, wireCancellationToken } from 'vs/base/common/async';
import { IModelService } from 'vs/editor/common/services/modelService';
import { LinkProviderRegistry, ILink } from 'vs/editor/common/modes';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { OUTPUT_MODE_ID } from 'vs/workbench/parts/output/common/output';
import { OUTPUT_MODE_ID, LOG_MODE_ID } from 'vs/workbench/parts/output/common/output';
import { MonacoWebWorker, createWebWorker } from 'vs/editor/common/services/webWorker';
import { ICreateData, OutputLinkComputer } from 'vs/workbench/parts/output/common/outputLinkComputer';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
@@ -44,7 +44,7 @@ export class OutputLinkProvider {
const folders = this.contextService.getWorkspace().folders;
if (folders.length > 0) {
if (!this.linkProviderRegistration) {
this.linkProviderRegistration = LinkProviderRegistry.register({ language: OUTPUT_MODE_ID, scheme: '*' }, {
this.linkProviderRegistration = LinkProviderRegistry.register([{ language: OUTPUT_MODE_ID, scheme: '*' }, { language: LOG_MODE_ID, scheme: '*' }], {
provideLinks: (model, token): Thenable<ILink[]> => {
return wireCancellationToken(token, this.provideLinks(model.uri));
}
@@ -13,7 +13,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { OutputService, LogContentProvider } from 'vs/workbench/parts/output/electron-browser/outputServices';
import { ToggleOutputAction, ClearOutputAction } from 'vs/workbench/parts/output/browser/outputActions';
import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, COMMAND_OPEN_LOG_VIEWER } from 'vs/workbench/parts/output/common/output';
import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, COMMAND_OPEN_LOG_VIEWER, LOG_MODE_ID, LOG_MIME } from 'vs/workbench/parts/output/common/output';
import { PanelRegistry, Extensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@@ -39,6 +39,14 @@ ModesRegistry.registerLanguage({
mimetypes: [OUTPUT_MIME]
});
// Register Log Output Mode
ModesRegistry.registerLanguage({
id: LOG_MODE_ID,
extensions: [],
aliases: [null],
mimetypes: [LOG_MIME]
});
// Register Output Panel
Registry.as<PanelRegistry>(Extensions.Panels).registerPanel(new PanelDescriptor(
OutputPanel,
@@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorOptions } from 'vs/workbench/common/editor';
import { IOutputChannelIdentifier, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, MAX_OUTPUT_LENGTH, LOG_SCHEME } from 'vs/workbench/parts/output/common/output';
import { IOutputChannelIdentifier, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, MAX_OUTPUT_LENGTH, LOG_SCHEME, LOG_MIME } from 'vs/workbench/parts/output/common/output';
import { OutputPanel } from 'vs/workbench/parts/output/browser/outputPanel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -134,6 +134,7 @@ abstract class AbstractFileOutputChannel extends Disposable {
constructor(
protected readonly outputChannelIdentifier: IOutputChannelIdentifier,
private readonly modelUri: URI,
private mimeType: string,
protected fileService: IFileService,
protected modelService: IModelService,
protected modeService: IModeService,
@@ -166,7 +167,7 @@ abstract class AbstractFileOutputChannel extends Disposable {
if (this.model) {
this.model.setValue(content);
} else {
this.model = this.modelService.createModel(content, this.modeService.getOrCreateMode(OUTPUT_MIME), this.modelUri);
this.model = this.modelService.createModel(content, this.modeService.getOrCreateMode(this.mimeType), this.modelUri);
this.onModelCreated(this.model);
const disposables: IDisposable[] = [];
disposables.push(this.model.onWillDispose(() => {
@@ -217,7 +218,7 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out
@IModeService modeService: IModeService,
@ILogService logService: ILogService
) {
super({ ...outputChannelIdentifier, file: URI.file(paths.join(outputDir, `${outputChannelIdentifier.id}.log`)) }, modelUri, fileService, modelService, modeService);
super({ ...outputChannelIdentifier, file: URI.file(paths.join(outputDir, `${outputChannelIdentifier.id}.log`)) }, modelUri, OUTPUT_MIME, fileService, modelService, modeService);
// Use one rotating file to check for main file reset
this.outputWriter = new RotatingLogger(this.id, this.file.fsPath, 1024 * 1024 * 30, 1);
@@ -363,7 +364,7 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann
@IModeService modeService: IModeService,
@ILogService logService: ILogService,
) {
super(outputChannelIdentifier, modelUri, fileService, modelService, modeService);
super(outputChannelIdentifier, modelUri, LOG_MIME, fileService, modelService, modeService);
this.fileHandler = this._register(new OutputFileListener(this.file));
this._register(this.fileHandler.onDidContentChange(() => this.onDidContentChange()));

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