Merge branch 'master' into joh/menus

This commit is contained in:
Johannes Rieken
2016-06-22 10:25:29 +02:00
89 changed files with 6038 additions and 1904 deletions
-2
View File
@@ -53,7 +53,6 @@ var vscodeResources = [
'out-build/vs/base/worker/workerMainCompatibility.html',
'out-build/vs/base/worker/workerMain.{js,js.map}',
'out-build/vs/base/browser/ui/octiconLabel/octicons/**',
'out-build/vs/languages/markdown/common/*.css',
'out-build/vs/workbench/browser/media/*-theme.css',
'out-build/vs/workbench/electron-browser/index.html',
'out-build/vs/workbench/parts/debug/**/*.json',
@@ -61,7 +60,6 @@ var vscodeResources = [
'out-build/vs/workbench/parts/git/**/*.html',
'out-build/vs/workbench/parts/git/**/*.sh',
'out-build/vs/workbench/parts/html/browser/webview.html',
'out-build/vs/workbench/parts/markdown/**/*.md',
'out-build/vs/workbench/parts/tasks/**/*.json',
'out-build/vs/workbench/parts/terminal/electron-browser/terminalProcess.js',
'out-build/vs/workbench/services/files/**/*.exe',
+1 -1
View File
@@ -69,7 +69,7 @@ export interface ICommandHandler {
declare module monaco.languages {
#includeAll(vs/editor/browser/standalone/standaloneLanguages;modes.=>;editorCommon.=>editor.):
#includeAll(vs/editor/browser/standalone/standaloneLanguages;modes.=>;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData):
#include(vs/editor/common/modes/languageConfigurationRegistry): CommentRule, LanguageConfiguration
#include(vs/editor/common/modes/supports/onEnter): IndentationRule, OnEnterRule
#include(vs/editor/common/modes/supports/electricCharacter): IBracketElectricCharacterContribution, IDocComment
@@ -1062,6 +1062,48 @@
</dict>
</array>
</dict>
<key>known-type-parameters</key>
<dict>
<key>begin</key>
<string>(&lt;)</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>meta.brace.angle.js</string>
</dict>
</dict>
<key>end</key>
<string>(?=$)|(&gt;)</string>
<key>endCaptures</key>
<dict>
<key>2</key>
<dict>
<key>name</key>
<string>meta.brace.angle.js</string>
</dict>
</dict>
<key>name</key>
<string>meta.known.type.parameters.ts</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\b(extends)\b</string>
<key>name</key>
<string>keyword.other.ts</string>
</dict>
<dict>
<key>include</key>
<string>#comment</string>
</dict>
<dict>
<key>include</key>
<string>#type</string>
</dict>
</array>
</dict>
<key>literal</key>
<dict>
<key>name</key>
@@ -1390,7 +1432,7 @@
</dict>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#know-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -1938,7 +1980,7 @@
</dict>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#known-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -1997,7 +2039,7 @@
<key>type-declaration</key>
<dict>
<key>begin</key>
<string>\b(type)\b\s+([a-zA-Z_$][\w$]*)\s*</string>
<string>\b(type)\b\s+([a-zA-Z_$][\w$]*)</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
@@ -2019,7 +2061,7 @@
<array>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#known-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -2130,7 +2172,7 @@
<key>type-parameters</key>
<dict>
<key>begin</key>
<string>JAVASCRIPT_DOES_NOT_HAVE_TYPE_PARAMETERS_SO_NEVER_MATCH_PLEASE([a-zA-Z_$][\w$]*)?(&lt;)</string>
<string>([a-zA-Z_$][\w$]*)?\s*(&lt;)(?=[^&lt;]*(&lt;[^&lt;&gt;]*&gt;)*&gt;\s*[(])</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
+6 -3
View File
@@ -4,6 +4,7 @@
"description": "Markdown for VS Code",
"version": "0.2.0",
"publisher": "Microsoft",
"aiKey":"AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
"vscode": "^1.0.0"
},
@@ -13,7 +14,8 @@
],
"activationEvents": [
"onCommand:markdown.showPreview",
"onCommand:markdown.showPreviewToSide"
"onCommand:markdown.showPreviewToSide",
"onCommand:markdown.showSource"
],
"contributes": {
"languages": [
@@ -53,7 +55,7 @@
},
{
"command": "markdown.showSource",
"title": "%markdown.previewMarkdown.title%",
"title": "%markdown.showSource.title%",
"category": "%markdown.category%",
"icon": {
"light": "./media/ViewSource.svg",
@@ -103,6 +105,7 @@
"dependencies": {
"highlight.js": "^9.3.0",
"markdown-it": "^6.0.1",
"markdown-it-named-headers": "0.0.4"
"markdown-it-named-headers": "0.0.4",
"vscode-extension-telemetry": "^0.0.5"
}
}
+3 -2
View File
@@ -1,6 +1,7 @@
{
"markdown.category" : "Markdown",
"markdown.openPreview" : "Open Preview",
"markdown.previewMarkdown.title" : "Toggle Preview",
"markdown.previewMarkdownSide.title" : "Open Preview to the Side"
"markdown.previewMarkdown.title" : "Show Preview",
"markdown.previewMarkdownSide.title" : "Open Preview to the Side",
"markdown.showSource.title" : "Show Source"
}
+70 -18
View File
@@ -7,23 +7,23 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { ExtensionContext, TextDocumentContentProvider, EventEmitter, Event, Uri, ViewColumn } from "vscode";
import { ExtensionContext, TextDocumentContentProvider, EventEmitter, Event, Uri, ViewColumn } from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
const hljs = require('highlight.js');
const mdnh = require('markdown-it-named-headers');
const md = require('markdown-it')({
html: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return `<pre class="hljs"><code><div>${hljs.highlight(lang, str, true).value}</div></code></pre>`;
} catch (error) { }
}
return `<pre class="hljs"><code><div>${md.utils.escapeHtml(str)}</div></code></pre>`;
}
}).use(mdnh, {});
interface IPackageInfo {
name: string;
version: string;
aiKey: string;
}
var telemetryReporter: TelemetryReporter;
export function activate(context: ExtensionContext) {
let packageInfo = getPackageInfo(context);
telemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
let provider = new MDDocumentContentProvider(context);
let registration = vscode.workspace.registerTextDocumentContentProvider('markdown', provider);
@@ -66,8 +66,9 @@ function getMarkdownUri(uri: Uri) {
return uri.with({ scheme: 'markdown', path: uri.path + '.rendered', query: uri.toString() });
}
function showPreview(resource?: Uri, sideBySide: boolean = false) {
function showPreview(uri?: Uri, sideBySide: boolean = false) {
let resource = uri;
if (!(resource instanceof Uri)) {
if (vscode.window.activeTextEditor) {
// we are relaxed and don't check for markdown files
@@ -76,14 +77,25 @@ function showPreview(resource?: Uri, sideBySide: boolean = false) {
}
if (!(resource instanceof Uri)) {
// nothing found that could be shown
if (!vscode.window.activeTextEditor) {
// this is most likely toggling the preview
return vscode.commands.executeCommand('markdown.showSource');
}
// nothing found that could be shown or toggled
return;
}
return vscode.commands.executeCommand('vscode.previewHtml',
let thenable = vscode.commands.executeCommand('vscode.previewHtml',
getMarkdownUri(resource),
getViewColumn(sideBySide),
`Preview '${path.basename(resource.fsPath)}'`);
telemetryReporter.sendTelemetryEvent('openPreview', {
where : sideBySide ? 'sideBySide' : 'inPlace',
how : (uri instanceof Uri) ? 'action' : 'pallete'
});
return thenable;
}
function getViewColumn(sideBySide): ViewColumn {
@@ -107,6 +119,10 @@ function getViewColumn(sideBySide): ViewColumn {
}
function showSource(mdUri: Uri) {
if (!mdUri) {
return vscode.commands.executeCommand('workbench.action.navigateBack');
}
const docUri = Uri.parse(mdUri.query);
for (let editor of vscode.window.visibleTextEditors) {
@@ -120,14 +136,50 @@ function showSource(mdUri: Uri) {
});
}
function getPackageInfo(context: ExtensionContext): IPackageInfo {
let extensionPackage = require(context.asAbsolutePath('./package.json'));
if (extensionPackage) {
return {
name: extensionPackage.name,
version: extensionPackage.version,
aiKey: extensionPackage.aiKey
};
}
return null;
}
interface IRenderer {
render(text: string) : string;
}
class MDDocumentContentProvider implements TextDocumentContentProvider {
private _context: ExtensionContext;
private _onDidChange = new EventEmitter<Uri>();
private _waiting : boolean;
private _renderer : IRenderer;
constructor(context: ExtensionContext) {
this._context = context;
this._waiting = false;
this._renderer = this.createRenderer();
}
private createRenderer() : IRenderer {
const hljs = require('highlight.js');
const mdnh = require('markdown-it-named-headers');
const md = require('markdown-it')({
html: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return `<pre class="hljs"><code><div>${hljs.highlight(lang, str, true).value}</div></code></pre>`;
} catch (error) { }
}
return `<pre class="hljs"><code><div>${md.utils.escapeHtml(str)}</div></code></pre>`;
}
}).use(mdnh, {});
return md;
}
private getMediaPath(mediaFile) {
@@ -171,7 +223,7 @@ class MDDocumentContentProvider implements TextDocumentContentProvider {
'<body>'
).join('\n');
const body = md.render(document.getText());
const body = this._renderer.render(document.getText());
const tail = [
'</body>',
@@ -0,0 +1,6 @@
declare module 'vscode-extension-telemetry' {
export default class TelemetryReporter {
constructor(extensionId: string,extensionVersion: string, key: string);
sendTelemetryEvent(eventName: string, properties?: { [key: string]: string }, measures?: { [key: string]: number }): void;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"account": "monacobuild",
"container": "debuggers",
"zip": "f9803b8/node-debug.zip",
"zip": "c9a5762/node-debug.zip",
"output": ""
}
@@ -695,6 +695,48 @@
<key>name</key>
<string>meta.indexer.parameter.ts</string>
</dict>
<key>known-type-parameters</key>
<dict>
<key>begin</key>
<string>(&lt;)</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>meta.brace.angle.ts</string>
</dict>
</dict>
<key>end</key>
<string>(?=$)|(&gt;)</string>
<key>endCaptures</key>
<dict>
<key>2</key>
<dict>
<key>name</key>
<string>meta.brace.angle.ts</string>
</dict>
</dict>
<key>name</key>
<string>meta.known.type.parameters.ts</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\b(extends)\b</string>
<key>name</key>
<string>keyword.other.ts</string>
</dict>
<dict>
<key>include</key>
<string>#comment</string>
</dict>
<dict>
<key>include</key>
<string>#type</string>
</dict>
</array>
</dict>
<key>literal</key>
<dict>
<key>name</key>
@@ -1023,7 +1065,7 @@
</dict>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#known-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -1571,7 +1613,7 @@
</dict>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#known-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -1630,7 +1672,7 @@
<key>type-declaration</key>
<dict>
<key>begin</key>
<string>\b(type)\b\s+([a-zA-Z_$][\w$]*)\s*</string>
<string>\b(type)\b\s+([a-zA-Z_$][\w$]*)</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
@@ -1652,7 +1694,7 @@
<array>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#known-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -1763,7 +1805,7 @@
<key>type-parameters</key>
<dict>
<key>begin</key>
<string>([a-zA-Z_$][\w$]*)?(&lt;)</string>
<string>([a-zA-Z_$][\w$]*)?\s*(&lt;)(?=[^&lt;]*(&lt;[^&lt;&gt;]*&gt;)*&gt;\s*[(])</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
@@ -1061,6 +1061,48 @@
</dict>
</array>
</dict>
<key>known-type-parameters</key>
<dict>
<key>begin</key>
<string>(&lt;)</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>meta.brace.angle.tsx</string>
</dict>
</dict>
<key>end</key>
<string>(?=$)|(&gt;)</string>
<key>endCaptures</key>
<dict>
<key>2</key>
<dict>
<key>name</key>
<string>meta.brace.angle.tsx</string>
</dict>
</dict>
<key>name</key>
<string>meta.known.type.parameters.ts</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\b(extends)\b</string>
<key>name</key>
<string>keyword.other.ts</string>
</dict>
<dict>
<key>include</key>
<string>#comment</string>
</dict>
<dict>
<key>include</key>
<string>#type</string>
</dict>
</array>
</dict>
<key>literal</key>
<dict>
<key>name</key>
@@ -1389,7 +1431,7 @@
</dict>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#know-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -1937,7 +1979,7 @@
</dict>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#known-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -1996,7 +2038,7 @@
<key>type-declaration</key>
<dict>
<key>begin</key>
<string>\b(type)\b\s+([a-zA-Z_$][\w$]*)\s*</string>
<string>\b(type)\b\s+([a-zA-Z_$][\w$]*)</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
@@ -2018,7 +2060,7 @@
<array>
<dict>
<key>include</key>
<string>#type-parameters</string>
<string>#known-type-parameters</string>
</dict>
<dict>
<key>include</key>
@@ -2129,7 +2171,7 @@
<key>type-parameters</key>
<dict>
<key>begin</key>
<string>([a-zA-Z_$][\w$]*)?(&lt;)</string>
<string>([a-zA-Z_$][\w$]*)?\s*(&lt;)(?=[^&lt;]*(&lt;[^&lt;&gt;]*&gt;)*&gt;\s*[(])</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
@@ -0,0 +1,17 @@
let keyCode = 0;
if(!(keyCode === 8 || (keyCode>=48 && keyCode<=57))) {}
for (let i=0; i<5; i++) {}
for (var i=0; i<5; i++) {}
for (let i=0; i<5; i++) {}
for (; i<5;) {}
for (let i=0; 1+( i<<5 ) < 5;i++) {}
var p = 1?2:(3<4?5:6);
class A<X, Y> { }
class A1<T extends { a: () => string }> { }
class B { }
class C { }
function foo<T>() { return 1;}
let x1: A<(param?: number) => void, B>;
let x2: A<C | B, C & B>;
const t = 1 < (5 > 10 ? 1 : 2);
var f6 = 1 < foo<string>();
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -83,7 +83,7 @@
"sinon": "^1.17.2",
"source-map": "^0.4.4",
"tslint": "^3.3.0",
"typescript": "^1.8.0",
"typescript": "^1.8.10",
"uglify-js": "2.4.8",
"underscore": "^1.8.2",
"vinyl": "^0.4.5",
-1
View File
@@ -6,6 +6,5 @@
"win32MutexName": "vscodeoss",
"licenseUrl": "https://github.com/Microsoft/vscode/blob/master/LICENSE.txt",
"darwinBundleIdentifier": "com.visualstudio.code.oss",
"welcomePage": "https://go.microsoft.com/fwlink/?LinkId=723048",
"reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new"
}
+11 -9
View File
@@ -90,16 +90,18 @@ if (!!process.send && process.env.PIPE_LOGGING === 'true') {
console.error = function () { safeSend({ type: '__$console', severity: 'error', arguments: safeStringify(arguments) }); };
}
// Let stdout, stderr and stdin be no-op streams. This prevents an issue where we would get an EBADF
// error when we are inside a forked process and this process tries to access those channels.
var stream = require('stream');
var writable = new stream.Writable({
write: function () { /* No OP */ }
});
if (!process.env['VSCODE_ALLOW_IO']) {
// Let stdout, stderr and stdin be no-op streams. This prevents an issue where we would get an EBADF
// error when we are inside a forked process and this process tries to access those channels.
var stream = require('stream');
var writable = new stream.Writable({
write: function () { /* No OP */ }
});
process.__defineGetter__('stdout', function() { return writable; });
process.__defineGetter__('stderr', function() { return writable; });
process.__defineGetter__('stdin', function() { return writable; });
process.__defineGetter__('stdout', function() { return writable; });
process.__defineGetter__('stderr', function() { return writable; });
process.__defineGetter__('stdin', function() { return writable; });
}
// Handle uncaught exceptions
process.on('uncaughtException', function (err) {
+11 -2
View File
@@ -922,7 +922,7 @@ export function append<T extends Node>(parent: HTMLElement, child: T): T {
const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/;
// Similar to builder, but much more lightweight
export function emmet(description: string): HTMLElement {
export function emmet<T extends HTMLElement>(description: string): T {
let match = SELECTOR_REGEX.exec(description);
if (!match) {
@@ -930,6 +930,7 @@ export function emmet(description: string): HTMLElement {
}
let result = document.createElement(match[1] || 'div');
if (match[3]) {
result.id = match[3];
}
@@ -937,7 +938,7 @@ export function emmet(description: string): HTMLElement {
result.className = match[4].replace(/\./g, ' ').trim();
}
return result;
return result as T;
}
export function show(...elements: HTMLElement[]): void {
@@ -985,4 +986,12 @@ export function removeTabIndexAndUpdateFocus(node: HTMLElement): void {
export function getElementsByTagName(tag: string): HTMLElement[] {
return Array.prototype.slice.call(document.getElementsByTagName(tag), 0);
}
export function finalHandler<T extends Event>(fn: (event: T)=>any): (event: T)=>any {
return e => {
e.preventDefault();
e.stopPropagation();
fn(e);
};
}
+127
View File
@@ -0,0 +1,127 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import _Event, { Emitter } from 'vs/base/common/event';
export interface IDomEvent {
(element: HTMLElement, type: "MSContentZoom", useCapture?: boolean): _Event<UIEvent>;
(element: HTMLElement, type: "MSGestureChange", useCapture?: boolean): _Event<MSGestureEvent>;
(element: HTMLElement, type: "MSGestureDoubleTap", useCapture?: boolean): _Event<MSGestureEvent>;
(element: HTMLElement, type: "MSGestureEnd", useCapture?: boolean): _Event<MSGestureEvent>;
(element: HTMLElement, type: "MSGestureHold", useCapture?: boolean): _Event<MSGestureEvent>;
(element: HTMLElement, type: "MSGestureStart", useCapture?: boolean): _Event<MSGestureEvent>;
(element: HTMLElement, type: "MSGestureTap", useCapture?: boolean): _Event<MSGestureEvent>;
(element: HTMLElement, type: "MSGotPointerCapture", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSInertiaStart", useCapture?: boolean): _Event<MSGestureEvent>;
(element: HTMLElement, type: "MSLostPointerCapture", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSManipulationStateChanged", useCapture?: boolean): _Event<MSManipulationEvent>;
(element: HTMLElement, type: "MSPointerCancel", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSPointerDown", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSPointerEnter", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSPointerLeave", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSPointerMove", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSPointerOut", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSPointerOver", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "MSPointerUp", useCapture?: boolean): _Event<MSPointerEvent>;
(element: HTMLElement, type: "abort", useCapture?: boolean): _Event<UIEvent>;
(element: HTMLElement, type: "activate", useCapture?: boolean): _Event<UIEvent>;
(element: HTMLElement, type: "ariarequest", useCapture?: boolean): _Event<AriaRequestEvent>;
(element: HTMLElement, type: "beforeactivate", useCapture?: boolean): _Event<UIEvent>;
(element: HTMLElement, type: "beforecopy", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "beforecut", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "beforedeactivate", useCapture?: boolean): _Event<UIEvent>;
(element: HTMLElement, type: "beforepaste", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "blur", useCapture?: boolean): _Event<FocusEvent>;
(element: HTMLElement, type: "canplay", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "canplaythrough", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "change", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "click", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "command", useCapture?: boolean): _Event<CommandEvent>;
(element: HTMLElement, type: "contextmenu", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "copy", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "cuechange", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "cut", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "dblclick", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "deactivate", useCapture?: boolean): _Event<UIEvent>;
(element: HTMLElement, type: "drag", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "dragend", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "dragenter", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "dragleave", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "dragover", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "dragstart", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "drop", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "durationchange", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "emptied", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "ended", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "error", useCapture?: boolean): _Event<ErrorEvent>;
(element: HTMLElement, type: "focus", useCapture?: boolean): _Event<FocusEvent>;
(element: HTMLElement, type: "gotpointercapture", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "input", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "keydown", useCapture?: boolean): _Event<KeyboardEvent>;
(element: HTMLElement, type: "keypress", useCapture?: boolean): _Event<KeyboardEvent>;
(element: HTMLElement, type: "keyup", useCapture?: boolean): _Event<KeyboardEvent>;
(element: HTMLElement, type: "load", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "loadeddata", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "loadedmetadata", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "loadstart", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "lostpointercapture", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "mousedown", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "mouseenter", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "mouseleave", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "mousemove", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "mouseout", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "mouseover", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "mouseup", useCapture?: boolean): _Event<MouseEvent>;
(element: HTMLElement, type: "mousewheel", useCapture?: boolean): _Event<MouseWheelEvent>;
(element: HTMLElement, type: "paste", useCapture?: boolean): _Event<DragEvent>;
(element: HTMLElement, type: "pause", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "play", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "playing", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "pointercancel", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "pointerdown", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "pointerenter", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "pointerleave", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "pointermove", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "pointerout", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "pointerover", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "pointerup", useCapture?: boolean): _Event<PointerEvent>;
(element: HTMLElement, type: "progress", useCapture?: boolean): _Event<ProgressEvent>;
(element: HTMLElement, type: "ratechange", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "reset", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "scroll", useCapture?: boolean): _Event<UIEvent>;
(element: HTMLElement, type: "seeked", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "seeking", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "select", useCapture?: boolean): _Event<UIEvent>;
(element: HTMLElement, type: "selectstart", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "stalled", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "submit", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "suspend", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "timeupdate", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "touchcancel", useCapture?: boolean): _Event<TouchEvent>;
(element: HTMLElement, type: "touchend", useCapture?: boolean): _Event<TouchEvent>;
(element: HTMLElement, type: "touchmove", useCapture?: boolean): _Event<TouchEvent>;
(element: HTMLElement, type: "touchstart", useCapture?: boolean): _Event<TouchEvent>;
(element: HTMLElement, type: "volumechange", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "waiting", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "webkitfullscreenchange", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "webkitfullscreenerror", useCapture?: boolean): _Event<Event>;
(element: HTMLElement, type: "wheel", useCapture?: boolean): _Event<WheelEvent>;
(element: HTMLElement, type: string, useCapture?: boolean): _Event<any>;
}
export const domEvent: IDomEvent = (element: HTMLElement, type: string, useCapture?) => {
const fn = e => emitter.fire(e);
const emitter = new Emitter<any>({
onFirstListenerAdd: () => {
element.addEventListener(type, fn);
},
onLastListenerRemove: () => {
element.removeEventListener(type, fn);
}
});
return emitter.event;
};
@@ -36,7 +36,7 @@
cursor: default;
}
.monaco-action-bar .action-item.active {
.monaco-action-bar.animated .action-item.active {
-ms-transform: translate(0, -3px);
-webkit-transform: translate(0, -3px);
-moz-transform: translate(0, -3px);
@@ -77,7 +77,7 @@
margin-right: .8em;
}
.monaco-action-bar.vertical .action-item.active {
.monaco-action-bar.animated.vertical .action-item.active {
-ms-transform: translate(5px, 0);
-webkit-transform: translate(5px, 0);
-moz-transform: translate(5px, 0);
@@ -137,7 +137,7 @@ export class BaseActionItem extends EventEmitter implements IActionItem {
public onClick(event: Event): void {
DOM.EventHelper.stop(event, true);
let context: any;
if (types.isUndefinedOrNull(this._context)) {
context = event;
@@ -388,6 +388,7 @@ export interface IActionBarOptions {
actionItemProvider?: IActionItemProvider;
actionRunner?: IActionRunner;
ariaLabel?: string;
animated?: boolean;
}
let defaultOptions: IActionBarOptions = {
@@ -440,6 +441,10 @@ export class ActionBar extends EventEmitter implements IActionRunner {
this.domNode = document.createElement('div');
this.domNode.className = 'monaco-action-bar';
if (options.animated !== false) {
DOM.addClass(this.domNode, 'animated');
}
let isVertical = this.options.orientation === ActionsOrientation.VERTICAL;
if (isVertical) {
this.domNode.className += ' vertical';
+5 -5
View File
@@ -47,12 +47,12 @@
.vs-dark .monaco-list-row:hover { background-color: rgba(255, 255, 255, 0.08); }
.hc-black .monaco-list-row:hover { outline: 1px dashed #f38518; background: transparent; }
/* Selection */
.monaco-list-row.selected { background-color: #4FA7FF; color: white; }
.vs-dark .monaco-list-row.selected { background-color: #0E639C; color: white; }
.hc-black .monaco-list-row.selected { border: 1px solid #f38518; }
/* Focus */
.monaco-list-row.focused { background-color: #DCEBFC; }
.vs-dark .monaco-list-row.focused { background-color: #073655; }
.hc-black .monaco-list-row.focused { outline: 1px dotted #f38518; background: transparent }
/* Selection */
.monaco-list-row.selected { background-color: #4FA7FF; color: white; }
.vs-dark .monaco-list-row.selected { background-color: #0E639C; color: white; }
.hc-black .monaco-list-row.selected { border: 1px solid #f38518; }
+124
View File
@@ -0,0 +1,124 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./list';
import { IDisposable } from 'vs/base/common/lifecycle';
import { range } from 'vs/base/common/arrays';
import { IDelegate, IRenderer, IFocusChangeEvent, ISelectionChangeEvent } from './list';
import { List } from './listWidget';
import { PagedModel } from 'vs/base/common/paging';
import Event, { mapEvent } from 'vs/base/common/event';
export interface IPagedRenderer<TElement, TTemplateData> extends IRenderer<TElement, TTemplateData> {
renderPlaceholder(index: number, templateData: TTemplateData): void;
}
export interface ITemplateData<T> {
data: T;
disposable: IDisposable;
}
class PagedRenderer<TElement, TTemplateData> implements IRenderer<number, ITemplateData<TTemplateData>> {
get templateId(): string { return this.renderer.templateId; }
constructor(
private renderer: IPagedRenderer<TElement, TTemplateData>,
private modelProvider: () => PagedModel<TElement>
) {}
renderTemplate(container: HTMLElement): ITemplateData<TTemplateData> {
const data = this.renderer.renderTemplate(container);
return { data, disposable: { dispose: () => {} } };
}
renderElement(index: number, _: number, data: ITemplateData<TTemplateData>): void {
data.disposable.dispose();
const model = this.modelProvider();
if (model.isResolved(index)) {
return this.renderer.renderElement(model.get(index), index, data.data);
}
const promise = model.resolve(index);
data.disposable = { dispose: () => promise.cancel() };
this.renderer.renderPlaceholder(index, data.data);
promise.done(entry => this.renderer.renderElement(entry, index, data.data));
}
disposeTemplate(data: ITemplateData<TTemplateData>): void {
data.disposable.dispose();
data.disposable = null;
this.renderer.disposeTemplate(data.data);
data.data = null;
}
}
export class PagedList<T> {
private list: List<number>;
private _model: PagedModel<T>;
get onDOMFocus(): Event<FocusEvent> { return this.list.onDOMFocus; }
constructor(
container: HTMLElement,
delegate: IDelegate<number>,
renderers: IPagedRenderer<T, any>[]
) {
const pagedRenderers = renderers.map(r => new PagedRenderer<T, ITemplateData<T>>(r, () => this.model));
this.list = new List(container, delegate, pagedRenderers);
}
get onFocusChange(): Event<IFocusChangeEvent<T>> {
return mapEvent(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
}
get onSelectionChange(): Event<ISelectionChangeEvent<T>> {
return mapEvent(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
}
get model(): PagedModel<T> {
return this._model;
}
set model(model: PagedModel<T>) {
this._model = model;
this.list.splice(0, this.list.length, ...range(model.length));
}
get scrollTop(): number {
return this.list.scrollTop;
}
focusNext(n?: number, loop?: boolean): void {
this.list.focusNext(n, loop);
}
focusPrevious(n?: number, loop?: boolean): void {
this.list.focusPrevious(n, loop);
}
selectNext(n?: number, loop?: boolean): void {
this.list.selectNext(n, loop);
}
selectPrevious(n?: number, loop?: boolean): void {
this.list.selectPrevious(n, loop);
}
getFocus(): number[] {
return this.list.getFocus();
}
setSelection(...indexes: number[]): void {
this.list.setSelection(...indexes);
}
layout(height?: number): void {
this.list.layout(height);
}
}
+12 -4
View File
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { toObject, assign } from 'vs/base/common/objects';
import { toObject, assign, getOrDefault } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Gesture } from 'vs/base/browser/touch';
import * as DOM from 'vs/base/browser/dom';
@@ -38,6 +38,14 @@ const MouseEventTypes = [
'contextmenu'
];
export interface IListViewOptions {
useShadows?: boolean;
}
const DefaultOptions: IListViewOptions = {
useShadows: true
};
export class ListView<T> implements IDisposable {
private items: IItem<T>[];
@@ -56,7 +64,8 @@ export class ListView<T> implements IDisposable {
constructor(
container: HTMLElement,
private delegate: IDelegate<T>,
renderers: IRenderer<T, any>[]
renderers: IRenderer<T, any>[],
options: IListViewOptions = DefaultOptions
) {
this.items = [];
this.itemId = 0;
@@ -69,7 +78,6 @@ export class ListView<T> implements IDisposable {
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-list';
this._domNode.tabIndex = 0;
this.rowsContainer = document.createElement('div');
this.rowsContainer.className = 'monaco-list-rows';
@@ -79,7 +87,7 @@ export class ListView<T> implements IDisposable {
canUseTranslate3d: false,
horizontal: ScrollbarVisibility.Hidden,
vertical: ScrollbarVisibility.Auto,
useShadows: false,
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows),
saveLastScrollTimeOnClassName: 'monaco-list-row'
});
+34 -7
View File
@@ -8,8 +8,9 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { isNumber } from 'vs/base/common/types';
import * as DOM from 'vs/base/browser/dom';
import Event, { Emitter, mapEvent, EventBufferer } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IDelegate, IRenderer, IListMouseEvent, IFocusChangeEvent, ISelectionChangeEvent } from './list';
import { ListView } from './listView';
import { ListView, IListViewOptions } from './listView';
interface ITraitTemplateData<D> {
container: HTMLElement;
@@ -138,6 +139,8 @@ class Controller<T> implements IDisposable {
private onClick(e: IListMouseEvent<T>) {
e.preventDefault();
e.stopPropagation();
this.view.domNode.focus();
this.list.setFocus(e.index);
this.list.setSelection(e.index);
}
@@ -146,6 +149,11 @@ class Controller<T> implements IDisposable {
}
}
export interface IListOptions extends IListViewOptions {
}
const DefaultOptions: IListOptions = {};
export class List<T> implements IDisposable {
private static InstanceCount = 0;
@@ -165,10 +173,14 @@ export class List<T> implements IDisposable {
return this.eventBufferer.wrapEvent(mapEvent(this.selection.onChange, e => this.toListEvent(e)));
}
private _onDOMFocus: Event<FocusEvent>;
get onDOMFocus(): Event<FocusEvent> { return this._onDOMFocus; }
constructor(
container: HTMLElement,
delegate: IDelegate<T>,
renderers: IRenderer<T, any>[]
renderers: IRenderer<T, any>[],
options: IListOptions = DefaultOptions
) {
this.focus = new FocusTrait(i => this.getElementId(i));
this.selection = new Trait('selected');
@@ -180,9 +192,12 @@ export class List<T> implements IDisposable {
return r;
});
this.view = new ListView(container, delegate, renderers);
this.view = new ListView(container, delegate, renderers, options);
this.view.domNode.setAttribute('role', 'listbox');
this.view.domNode.tabIndex = 0;
this.controller = new Controller(this, this.view);
this._onDOMFocus = domEvent(this.view.domNode, 'focus');
}
splice(start: number, deleteCount: number, ...elements: T[]): void {
@@ -201,6 +216,10 @@ export class List<T> implements IDisposable {
return this.view.getContentHeight();
}
get scrollTop(): number {
return this.view.getScrollTop();
}
layout(height?: number): void {
this.view.layout(height);
}
@@ -229,6 +248,10 @@ export class List<T> implements IDisposable {
this.setSelection(Math.max(index, 0));
}
getSelection(): number[] {
return this.selection.get();
}
setFocus(...indexes: number[]): void {
this.eventBufferer.bufferEvents(() => {
indexes = indexes.concat(this.focus.set(...indexes));
@@ -255,7 +278,7 @@ export class List<T> implements IDisposable {
let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight);
lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1;
const lastPageElement = this.view.element(lastPageIndex);
const currentlyFocusedElement = this.getFocus()[0];
const currentlyFocusedElement = this.getFocusedElements()[0];
if (currentlyFocusedElement !== lastPageElement) {
this.setFocus(lastPageIndex);
@@ -281,7 +304,7 @@ export class List<T> implements IDisposable {
}
const firstPageElement = this.view.element(firstPageIndex);
const currentlyFocusedElement = this.getFocus()[0];
const currentlyFocusedElement = this.getFocusedElements()[0];
if (currentlyFocusedElement !== firstPageElement) {
this.setFocus(firstPageIndex);
@@ -296,8 +319,12 @@ export class List<T> implements IDisposable {
}
}
getFocus(): T[] {
return this.focus.get().map(i => this.view.element(i));
getFocus(): number[] {
return this.focus.get();
}
getFocusedElements(): T[] {
return this.getFocus().map(i => this.view.element(i));
}
reveal(index: number, relativeTop?: number): void {
+16
View File
@@ -216,10 +216,26 @@ export function flatten<T>(arr: T[][]): T[] {
return arr.reduce((r, v) => r.concat(v), []);
}
export function range(to: number, from = 0): number[] {
const result = [];
for (let i = from; i < to; i++) {
result.push(i);
}
return result;
}
export function fill<T>(num: number, valueFn: () => T, arr: T[] = []): T[] {
for (let i = 0; i < num; i++) {
arr[i] = valueFn();
}
return arr;
}
export function index<T>(array: T[], indexer: (t: T) => string): { [key: string]: T; } {
const result = Object.create(null);
array.forEach(t => result[indexer(t)] = t);
return result;
}
+5 -2
View File
@@ -152,8 +152,11 @@ export function fromEventEmitter<T>(emitter: EventEmitter, eventType: string): E
}
export function mapEvent<I,O>(event: Event<I>, map: (i:I)=>O): Event<O> {
return (listener, thisArgs?, disposables?) =>
event(i => listener(map(i)), thisArgs, disposables);
return (listener, thisArgs?, disposables?) => event(i => listener(map(i)), thisArgs, disposables);
}
export function filterEvent<T>(event: Event<T>, filter: (e:T)=>boolean): Event<T> {
return (listener, thisArgs?, disposables?) => event(e => filter(e) && listener(e), thisArgs, disposables);
}
enum EventDelayerState {
+29 -1
View File
@@ -8,6 +8,9 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { ArraySet } from 'vs/base/common/set';
/**
* A Pager is a stateless abstraction over a paged collection.
*/
export interface IPager<T> {
firstPage: T[];
total: number;
@@ -22,7 +25,17 @@ interface IPage<T> {
elements: T[];
}
export class PagedModel<T> {
/**
* A PagedModel is a stateful model over an abstracted paged collection.
*/
export interface IPagedModel<T> {
length: number;
isResolved(index: number): boolean;
get(index: number): T;
resolve(index: number): TPromise<T>;
}
export class PagedModel<T> implements IPagedModel<T> {
private pages: IPage<T>[] = [];
@@ -92,6 +105,21 @@ export class PagedModel<T> {
}
}
export class SinglePagePagedModel<T> extends PagedModel<T> {
constructor(elements: T[]) {
super({
firstPage: elements,
total: elements.length,
pageSize: elements.length,
getPage: null
});
}
}
/**
* Similar to array.map, `mapPager` lets you map the elements of an
* abstract paged collection to another type.
*/
export function mapPager<T,R>(pager: IPager<T>, fn: (t: T) => R): IPager<R> {
return {
firstPage: pager.firstPage.map(fn),
+17
View File
@@ -86,6 +86,23 @@ export function download(filePath: string, opts: IRequestOptions): TPromise<void
}));
}
export function text(opts: IRequestOptions): TPromise<string> {
return request(opts).then(pair => new Promise((c, e) => {
if (!((pair.res.statusCode >= 200 && pair.res.statusCode < 300) || pair.res.statusCode === 1223)) {
return e('Server returned ' + pair.res.statusCode);
}
if (pair.res.statusCode === 204) {
return c(null);
}
let buffer: string[] = [];
pair.res.on('data', d => buffer.push(d));
pair.res.on('end', () => c(buffer.join('')));
pair.res.on('error', e);
}));
}
export function json<T>(opts: IRequestOptions): TPromise<T> {
return request(opts).then(pair => new Promise((c, e) => {
if (!((pair.res.statusCode >= 200 && pair.res.statusCode < 300) || pair.res.statusCode === 1223)) {
@@ -575,12 +575,19 @@ export class QuickOpenWidget implements IModelProvider {
}
}
// Finally check for auto focus of second entry
// Check for auto focus of second entry
else if (autoFocus.autoFocusSecondEntry) {
if (entries.length > 1) {
this.tree.focusNth(1);
}
}
// Finally check for auto focus of last entry
else if (autoFocus.autoFocusLastEntry) {
if (entries.length > 1) {
this.tree.focusLast();
}
}
}
public refresh(input: IModel<any>, autoFocus: IAutoFocus): void {
@@ -719,8 +726,8 @@ export class QuickOpenWidget implements IModelProvider {
}
}
public isQuickNavigating(): boolean {
return !!this.quickNavigateConfiguration;
public getQuickNavigateConfiguration(): IQuickNavigateConfiguration {
return this.quickNavigateConfiguration;
}
public setPlaceHolder(placeHolder: string): void {
@@ -27,6 +27,11 @@ export interface IAutoFocus {
*/
autoFocusSecondEntry?: boolean;
/**
* If set to true, will automatically select the last entry from the result list.
*/
autoFocusLastEntry?: boolean;
/**
* If set to true, will automatically select any entry whose label starts with the search
* value. Since some entries to the top might match the query but not on the prefix, this
+1 -1
View File
@@ -109,7 +109,7 @@ function main(accessor: ServicesAccessor, ipcServer: Server, userEnv: IProcessEn
process.env['VSCODE_SHARED_IPC_HOOK'] = envService.sharedIPCHandle;
// Spawn shared process
const sharedProcess = instantiationService.invokeFunction(spawnSharedProcess);
const sharedProcess = spawnSharedProcess(!envService.isBuilt || envService.cliArgs.verboseLogging);
// Make sure we associate the program with the app user model id
// This will help Windows to associate the running program with
+4 -2
View File
@@ -20,7 +20,7 @@ import product from 'vs/platform/product';
import pkg from 'vs/platform/package';
export function generateNewIssueUrl(baseUrl: string, name: string, version: string, commit: string, date: string): string {
const osVersion = `${ os.type() } ${ os.arch() }`;
const osVersion = `${ os.type() } ${ os.arch() } ${ os.release() }`;
const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&';
const body = encodeURIComponent(
`- VSCode Version: ${name} ${version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})
@@ -592,12 +592,14 @@ export class VSCodeMenu {
let nextEditor = this.createMenuItem(nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor"), 'workbench.action.nextEditor');
let previousEditor = this.createMenuItem(nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor"), 'workbench.action.previousEditor');
let previousEditorInGroup = this.createMenuItem(nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "Previous &&Editor in Group"), 'workbench.action.openPreviousEditorInGroup');
let nextEditorInGroup = this.createMenuItem(nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group"), 'workbench.action.openNextRecentlyUsedEditorInGroup');
let previousEditorInGroup = this.createMenuItem(nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group"), 'workbench.action.openPreviousRecentlyUsedEditorInGroup');
[
nextEditor,
previousEditor,
__separator__(),
nextEditorInGroup,
previousEditorInGroup
].forEach(item => switchEditorMenu.append(item));
+7 -4
View File
@@ -7,15 +7,18 @@ import * as cp from 'child_process';
import URI from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { assign } from 'vs/base/common/objects';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
const boostrapPath = URI.parse(require.toUrl('bootstrap')).fsPath;
function _spawnSharedProcess(): cp.ChildProcess {
function _spawnSharedProcess(allowOutput: boolean): cp.ChildProcess {
const env = assign({}, process.env, {
AMD_ENTRYPOINT: 'vs/code/node/sharedProcessMain'
});
if (allowOutput) {
env['VSCODE_ALLOW_IO'] = 'true';
}
const result = cp.fork(boostrapPath, ['--type=SharedProcess'], { env });
// handshake
@@ -26,7 +29,7 @@ function _spawnSharedProcess(): cp.ChildProcess {
let spawnCount = 0;
export function spawnSharedProcess(accessor: ServicesAccessor): IDisposable {
export function spawnSharedProcess(allowOutput: boolean): IDisposable {
let child: cp.ChildProcess;
const spawn = () => {
@@ -34,7 +37,7 @@ export function spawnSharedProcess(accessor: ServicesAccessor): IDisposable {
return;
}
child = _spawnSharedProcess();
child = _spawnSharedProcess(allowOutput);
child.on('exit', spawn);
};
+13 -9
View File
@@ -9,6 +9,7 @@ import pkg from 'vs/platform/package';
import { ParsedArgs } from 'vs/code/node/argv';
import { TPromise } from 'vs/base/common/winjs.base';
import { sequence } from 'vs/base/common/async';
import { IPager } from 'vs/base/common/paging';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -17,8 +18,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { IEventService } from 'vs/platform/event/common/event';
import { EventService } from 'vs/platform/event/common/eventService';
import { IExtensionManagementService, IExtensionGalleryService, IQueryResult } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { IExtensionManagementService, IExtensionGalleryService, IExtensionManifest, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { ITelemetryService, combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -34,6 +34,10 @@ const notFound = id => localize('notFound', "Extension '{0}' not found.", id);
const notInstalled = id => localize('notInstalled', "Extension '{0}' is not installed.", id);
const useId = localize('useId', "Make sure you use the full extension ID, eg: {0}", 'ms-vscode.csharp');
function getId(manifest: IExtensionManifest): string {
return `${ manifest.publisher }.${ manifest.name }`;
}
class Main {
constructor(
@@ -59,22 +63,22 @@ class Main {
private listExtensions(): TPromise<any> {
return this.extensionManagementService.getInstalled().then(extensions => {
extensions.forEach(e => console.log(getExtensionId(e)));
extensions.forEach(e => console.log(getId(e.manifest)));
});
}
private installExtension(ids: string[]): TPromise<any> {
return sequence(ids.map(id => () => {
return this.extensionManagementService.getInstalled().then(installed => {
const isInstalled = installed.some(e => getExtensionId(e) === id);
const isInstalled = installed.some(e => getId(e.manifest) === id);
if (isInstalled) {
console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", id));
return TPromise.as(null);
}
return this.extensionGalleryService.query({ ids: [id] })
.then<IQueryResult>(null, err => {
return this.extensionGalleryService.query({ names: [id] })
.then<IPager<IGalleryExtension>>(null, err => {
if (err.responseText) {
try {
const response = JSON.parse(err.responseText);
@@ -94,8 +98,8 @@ class Main {
console.log(localize('foundExtension', "Found '{0}' in the marketplace.", id));
console.log(localize('installing', "Installing..."));
return this.extensionManagementService.install(extension).then(extension => {
console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.version));
return this.extensionManagementService.install(extension).then(() => {
console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.versions[0].version));
});
});
});
@@ -105,7 +109,7 @@ class Main {
private uninstallExtension(ids: string[]): TPromise<any> {
return sequence(ids.map(id => () => {
return this.extensionManagementService.getInstalled().then(installed => {
const [extension] = installed.filter(e => getExtensionId(e) === id);
const [extension] = installed.filter(e => getId(e.manifest) === id);
if (!extension) {
return TPromise.wrapError(`${ notInstalled(id) }\n${ useId }`);
@@ -26,6 +26,7 @@ import {toThenable} from 'vs/base/common/async';
import {compile} from 'vs/editor/common/modes/monarch/monarchCompile';
import {createTokenizationSupport} from 'vs/editor/common/modes/monarch/monarchLexer';
import {LanguageConfigurationRegistry} from 'vs/editor/common/modes/languageConfigurationRegistry';
import {IMarkerData} from 'vs/platform/markers/common/markers';
/**
* Register information about a new language.
@@ -148,8 +149,17 @@ export function registerCodeLensProvider(languageId:string, provider:modes.CodeL
/**
* Register a code action provider (used by e.g. quick fix).
*/
export function registerCodeActionProvider(languageId:string, provider:modes.CodeActionProvider): IDisposable {
return modes.CodeActionProviderRegistry.register(languageId, provider);
export function registerCodeActionProvider(languageId:string, provider:CodeActionProvider): IDisposable {
return modes.CodeActionProviderRegistry.register(languageId, {
provideCodeActions: (model:editorCommon.IReadOnlyModel, range:Range, token: CancellationToken): modes.CodeAction[] | Thenable<modes.CodeAction[]> => {
startup.initStaticServicesIfNecessary();
var markerService = ensureStaticPlatformServices(null).markerService;
let markers = markerService.read({resource: model.uri }).filter(m => {
return Range.areIntersectingOrTouching(m, range);
});
return provider.provideCodeActions(model, range, { markers }, token);
}
});
}
/**
@@ -197,6 +207,31 @@ export function registerCompletionItemProvider(languageId:string, provider:Compl
});
}
/**
* Contains additional diagnostic information about the context in which
* a [code action](#CodeActionProvider.provideCodeActions) is run.
*/
export interface CodeActionContext {
/**
* An array of diagnostics.
*
* @readonly
*/
markers: IMarkerData[];
}
/**
* The code action interface defines the contract between extensions and
* the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
*/
export interface CodeActionProvider {
/**
* Provide commands for the given document and range.
*/
provideCodeActions(model:editorCommon.IReadOnlyModel, range:Range, context: CodeActionContext, token: CancellationToken): modes.CodeAction[] | Thenable<modes.CodeAction[]>;
}
/**
* Completion item kinds.
*/
@@ -396,7 +431,7 @@ class SuggestAdapter {
let isSingleLine = (editRange.startLineNumber === editRange.endLineNumber);
// invalid text edit
if (!isSingleLine || editRange.startColumn !== position.lineNumber) {
if (!isSingleLine || editRange.startLineNumber !== position.lineNumber) {
console.warn('INVALID text edit, must be single line and on the same line');
continue;
}
+4 -2
View File
@@ -231,10 +231,12 @@ registerCoreCommand(H.ScrollLineDown, {
});
registerCoreCommand(H.ScrollPageUp, {
primary: KeyMod.CtrlCmd | KeyCode.PageUp
primary: KeyMod.CtrlCmd | KeyCode.PageUp,
win: { primary: KeyMod.Alt | KeyCode.PageUp }
});
registerCoreCommand(H.ScrollPageDown, {
primary: KeyMod.CtrlCmd | KeyCode.PageDown
primary: KeyMod.CtrlCmd | KeyCode.PageDown,
win: { primary: KeyMod.Alt | KeyCode.PageDown }
});
registerCoreCommand(H.CursorColumnSelectLeft, {
+1
View File
@@ -418,6 +418,7 @@ export interface CodeAction {
/**
* The code action interface defines the contract between extensions and
* the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
* @internal
*/
export interface CodeActionProvider {
/**
@@ -822,7 +822,7 @@ class SnippetController implements ISnippetController {
}
}
private static _prepareSnippet(editor:editorCommon.ICommonCodeEditor, selection:Selection, snippet:CodeSnippet, overwriteBefore:number, overwriteAfter:number, stripPrefix?:boolean): { typeRange: Range; adaptedSnippet: ICodeSnippet; } {
private static _prepareSnippet(editor:editorCommon.ICommonCodeEditor, selection:Selection, snippet:CodeSnippet, overwriteBefore:number, overwriteAfter:number, stripPrefix = true): { typeRange: Range; adaptedSnippet: ICodeSnippet; } {
var model = editor.getModel();
var typeRange = SnippetController._getTypeRangeForSelection(model, selection, overwriteBefore, overwriteAfter);
@@ -831,7 +831,7 @@ class SnippetController implements ISnippetController {
var nextInSnippet = snippet.lines[0].substr(overwriteBefore);
var commonPrefix = strings.commonPrefixLength(nextTextOnLine, nextInSnippet);
if (commonPrefix > 0 && !stripPrefix === false) {
if (commonPrefix > 0 && stripPrefix) {
typeRange = typeRange.setEndPosition(typeRange.endLineNumber, typeRange.endColumn + commonPrefix);
}
}
@@ -148,7 +148,7 @@ class Delegate implements IDelegate<CompletionItem> {
constructor(private listProvider: () => List<CompletionItem>) { }
getHeight(element: CompletionItem): number {
const focus = this.listProvider().getFocus()[0];
const focus = this.listProvider().getFocusedElements()[0];
if (element.suggestion.documentationLabel && element === focus) {
return FocusHeight;
@@ -369,7 +369,9 @@ export class SuggestWidget implements IContentWidget, IDisposable {
let renderer: IRenderer<CompletionItem, any> = instantiationService.createInstance(Renderer, this, this.editor);
this.delegate = new Delegate(() => this.list);
this.list = new List(this.listElement, this.delegate, [renderer]);
this.list = new List(this.listElement, this.delegate, [renderer], {
useShadows: false
});
this.toDispose = [
editor.onDidBlurEditorText(() => this.onEditorBlur()),
@@ -738,7 +740,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
case State.Loading:
return !this.isAuto;
default:
const focus = this.list.getFocus()[0];
const focus = this.list.getFocusedElements()[0];
if (focus) {
this.list.setSelection(this.completionModel.items.indexOf(focus));
} else {
@@ -759,7 +761,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
return;
}
const item = this.list.getFocus()[0];
const item = this.list.getFocusedElements()[0];
if (!item || !item.suggestion.documentationLabel) {
return;
@@ -823,7 +825,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
} else if (this.state === State.Details) {
height = 12 * UnfocusedHeight;
} else {
const focus = this.list.getFocus()[0];
const focus = this.list.getFocusedElements()[0];
const focusHeight = focus ? this.delegate.getHeight(focus) : UnfocusedHeight;
height = focusHeight;
@@ -842,7 +844,7 @@ export class SuggestWidget implements IContentWidget, IDisposable {
if (this.state !== State.Details) {
this.details.render(null);
} else {
this.details.render(this.list.getFocus()[0]);
this.details.render(this.list.getFocusedElements()[0]);
}
}
-3
View File
@@ -65,9 +65,6 @@ exports.collectModules = function(args) {
.combine(worker)
.define('vs/languages/html/common/htmlWorker', ['vs/languages/lib/common/beautify-html']);
// ---- markdown -------------------------------
common.define('vs/languages/markdown/common/markdown', ['vs/languages/html/common/html']);
// ---- php -----------------------------------
common.define('vs/languages/php/common/php');
-1
View File
@@ -8,5 +8,4 @@
import 'vs/languages/handlebars/common/handlebars.contribution';
import 'vs/languages/html/common/html.contribution';
import 'vs/languages/markdown/common/markdown.contribution';
import 'vs/languages/razor/common/razor.contribution';
@@ -1,19 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {ModesRegistry} from 'vs/editor/common/modes/modesRegistry';
const register = false;
if (register) {
ModesRegistry.registerCompatMode({
id: 'markdown',
extensions: ['.md', '.markdown', '.mdown', '.mkdn', '.mkd', '.mdwn', '.mdtxt', '.mdtext'],
aliases: ['Markdown', 'markdown'],
mimetypes: ['text/x-web-markdown'],
moduleId: 'vs/languages/markdown/common/markdown',
ctorName: 'MarkdownMode'
});
}
@@ -1,239 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Types = require('vs/editor/common/modes/monarch/monarchTypes');
import Compile = require('vs/editor/common/modes/monarch/monarchCompile');
import Modes = require('vs/editor/common/modes');
import {htmlTokenTypes} from 'vs/languages/html/common/html';
import {IModeService} from 'vs/editor/common/services/modeService';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IThreadService} from 'vs/platform/thread/common/thread';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService';
import {AbstractMode} from 'vs/editor/common/modes/abstractMode';
import {createTokenizationSupport} from 'vs/editor/common/modes/monarch/monarchLexer';
import {LanguageConfigurationRegistry, LanguageConfiguration} from 'vs/editor/common/modes/languageConfigurationRegistry';
import {wireCancellationToken} from 'vs/base/common/async';
export const TOKEN_HEADER_LEAD = 'entity.name.tag';
export const TOKEN_HEADER = 'entity.name.tag';
export const TOKEN_EXT_HEADER = 'entity.other.attribute-name';
export const TOKEN_SEPARATOR = 'meta.separator';
export const TOKEN_QUOTE = 'comment';
export const TOKEN_LIST = 'keyword';
export const TOKEN_BLOCK = 'string';
export const TOKEN_BLOCK_CODE = 'variable.source';
export const language =
<Types.IMonarchLanguage>{
defaultToken: '',
tokenPostfix: '.md',
// escape codes
control: /[\\`*_\[\]{}()#+\-\.!]/,
noncontrol: /[^\\`*_\[\]{}()#+\-\.!]/,
escapes: /\\(?:@control)/,
// escape codes for javascript/CSS strings
jsescapes: /\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,
// non matched elements
empty: [
'area', 'base', 'basefont', 'br', 'col', 'frame',
'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'
],
tokenizer: {
root: [
// headers (with #)
[/^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/, ['white', TOKEN_HEADER_LEAD, TOKEN_HEADER, TOKEN_HEADER]],
// headers (with =)
[/^\s*(=+|\-+)\s*$/, TOKEN_EXT_HEADER],
// headers (with ***)
[/^\s*((\*[ ]?)+)\s*$/, TOKEN_SEPARATOR],
// quote
[/^\s*>+/, TOKEN_QUOTE],
// list (starting with * or number)
[/^\s*([\*\-+:]|\d+\.)\s/, TOKEN_LIST],
// code block (4 spaces indent)
[/^(\t|[ ]{4})[^ ].*$/, TOKEN_BLOCK],
// code block (3 tilde)
[/^\s*~{3}\s*((?:\w|[\/\-#])+)?\s*$/, { token: TOKEN_BLOCK, next: '@codeblock' }],
// github style code blocks (with backticks and language)
[/^\s*```\s*((?:\w|[\/\-#])+)\s*$/, { token: TOKEN_BLOCK, next: '@codeblockgh', nextEmbedded: '$1' }],
// github style code blocks (with backticks but no language)
[/^\s*`{3}\s*$/, { token: TOKEN_BLOCK, next: '@codeblock' }],
// markup within lines
{ include: '@linecontent' },
],
codeblock: [
[/^\s*~{3}\s*$/, { token: TOKEN_BLOCK, next: '@pop' }],
[/^\s*`{3}\s*$/, { token: TOKEN_BLOCK, next: '@pop' }],
[/.*$/, TOKEN_BLOCK_CODE],
],
// github style code blocks
codeblockgh: [
[/```\s*$/, { token: '@rematch', switchTo: '@codeblockghend', nextEmbedded: '@pop' }],
[/[^`]*$/, TOKEN_BLOCK_CODE],
],
codeblockghend: [
[/\s*```/, { token: TOKEN_BLOCK_CODE, next: '@pop' }],
[/./, '@rematch', '@pop'],
],
linecontent: [
// escapes
[/&\w+;/, 'string.escape'],
[/@escapes/, 'escape'],
// various markup
[/\b__([^\\_]|@escapes|_(?!_))+__\b/, 'strong'],
[/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/, 'strong'],
[/\b_[^_]+_\b/, 'emphasis'],
[/\*([^\\*]|@escapes)+\*/, 'emphasis'],
[/`([^\\`]|@escapes)+`/, 'variable'],
// links
[/\{[^}]+\}/, 'string.target'],
[/(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/, ['string.link', '', 'string.link']],
[/(!?\[)((?:[^\]\\]|@escapes)*)(\])/, 'string.link'],
// or html
{ include: 'html' },
],
// Note: it is tempting to rather switch to the real HTML mode instead of building our own here
// but currently there is a limitation in Monarch that prevents us from doing it: The opening
// '<' would start the HTML mode, however there is no way to jump 1 character back to let the
// HTML mode also tokenize the opening angle bracket. Thus, even though we could jump to HTML,
// we cannot correctly tokenize it in that mode yet.
html: [
// html tags
[/<(\w+)\/>/, htmlTokenTypes.getTag('$1')],
[/<(\w+)/, {
cases: {
'@empty': { token: htmlTokenTypes.getTag('$1'), next: '@tag.$1' },
'@default': { token: htmlTokenTypes.getTag('$1'), bracket: '@open', next: '@tag.$1' }
}
}],
[/<\/(\w+)\s*>/, { token: htmlTokenTypes.getTag('$1'), bracket: '@close' }],
[/<!--/, 'comment', '@comment']
],
comment: [
[/[^<\-]+/, 'comment.content'],
[/-->/, 'comment', '@pop'],
[/<!--/, 'comment.content.invalid'],
[/[<\-]/, 'comment.content']
],
// Almost full HTML tag matching, complete with embedded scripts & styles
tag: [
[/[ \t\r\n]+/, 'white'],
[/(type)(\s*=\s*)(")([^"]+)(")/, [htmlTokenTypes.ATTRIB_NAME, htmlTokenTypes.DELIM_ASSIGN, htmlTokenTypes.ATTRIB_VALUE,
{ token: htmlTokenTypes.ATTRIB_VALUE, switchTo: '@tag.$S2.$4' },
htmlTokenTypes.ATTRIB_VALUE]],
[/(type)(\s*=\s*)(')([^']+)(')/, [htmlTokenTypes.ATTRIB_NAME, htmlTokenTypes.DELIM_ASSIGN, htmlTokenTypes.ATTRIB_VALUE,
{ token: htmlTokenTypes.ATTRIB_VALUE, switchTo: '@tag.$S2.$4' },
htmlTokenTypes.ATTRIB_VALUE]],
[/(\w+)(\s*=\s*)("[^"]*"|'[^']*')/, [htmlTokenTypes.ATTRIB_NAME, htmlTokenTypes.DELIM_ASSIGN, htmlTokenTypes.ATTRIB_VALUE]],
[/\w+/, htmlTokenTypes.ATTRIB_NAME],
[/\/>/, htmlTokenTypes.getTag('$S2'), '@pop'],
[/>/, {
cases: {
'$S2==style': { token: htmlTokenTypes.getTag('$S2'), switchTo: '@embedded.$S2', nextEmbedded: 'text/css' },
'$S2==script': {
cases: {
'$S3': { token: htmlTokenTypes.getTag('$S2'), switchTo: '@embedded.$S2', nextEmbedded: '$S3' },
'@default': { token: htmlTokenTypes.getTag('$S2'), switchTo: '@embedded.$S2', nextEmbedded: 'text/javascript' }
}
},
'@default': { token: htmlTokenTypes.getTag('$S2'), next: '@pop' }
}
}],
],
embedded: [
[/[^"'<]+/, ''],
[/<\/(\w+)\s*>/, {
cases: {
'$1==$S2': { token: '@rematch', next: '@pop', nextEmbedded: '@pop' },
'@default': ''
}
}],
[/"([^"\\]|\\.)*$/, 'string.invalid'], // non-teminated string
[/'([^'\\]|\\.)*$/, 'string.invalid'], // non-teminated string
[/"/, 'string', '@string."'],
[/'/, 'string', '@string.\''],
[/</, '']
],
// scan embedded strings in javascript or css
string: [
[/[^\\"']+/, 'string'],
[/@jsescapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/["']/, {
cases: {
'$#==$S2': { token: 'string', next: '@pop' },
'@default': 'string'
}
}]
]
}
};
export class MarkdownMode extends AbstractMode {
public static LANG_CONFIG: LanguageConfiguration = {
comments: {
blockComment: ['<!--', '-->',]
},
brackets: [['{', '}'], ['[', ']'], ['(', ')'], ['<', '>']],
autoClosingPairs: []
};
public tokenizationSupport: Modes.ITokenizationSupport;
constructor(
descriptor: Modes.IModeDescriptor,
@IInstantiationService instantiationService: IInstantiationService,
@IThreadService threadService: IThreadService,
@IModeService modeService: IModeService,
@IEditorWorkerService editorWorkerService: IEditorWorkerService,
@IConfigurationService configurationService: IConfigurationService
) {
super(descriptor.id);
let lexer = Compile.compile(descriptor.id, language);
this.tokenizationSupport = createTokenizationSupport(modeService, this, lexer);
LanguageConfigurationRegistry.register(this.getId(), MarkdownMode.LANG_CONFIG);
Modes.SuggestRegistry.register(this.getId(), {
triggerCharacters: [],
shouldAutotriggerSuggest: false,
provideCompletionItems: (model, position, token) => {
return wireCancellationToken(token, editorWorkerService.textualSuggest(model.uri, position));
}
}, true);
}
}
@@ -1,116 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import modesUtil = require('vs/editor/test/common/modesUtil');
import Modes = require('vs/editor/common/modes');
import {htmlTokenTypes} from 'vs/languages/html/common/html';
import {MockModeService} from 'vs/editor/test/common/mocks/mockModeService';
import {NULL_THREAD_SERVICE} from 'vs/platform/test/common/nullThreadService';
import {IThreadService} from 'vs/platform/thread/common/thread';
import {IModeService} from 'vs/editor/common/services/modeService';
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService';
import {MarkdownMode} from 'vs/languages/markdown/common/markdown';
import {MockTokenizingMode} from 'vs/editor/test/common/mocks/mockMode';
class MarkdownMockModeService extends MockModeService {
isRegisteredMode(mimetypeOrModeId: string): boolean {
if (mimetypeOrModeId === 'javascript') {
return true;
}
if (mimetypeOrModeId === 'css') {
return true;
}
throw new Error('Not implemented');
}
getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): Modes.IMode {
if (commaSeparatedMimetypesOrCommaSeparatedIds === 'javascript') {
return new MockTokenizingMode('js', 'mock-js');
}
if (commaSeparatedMimetypesOrCommaSeparatedIds === 'css') {
return new MockTokenizingMode('css', 'mock-css');
}
throw new Error('Not implemented');
}
getModeIdForLanguageName(alias:string): string {
if (alias === 'text/javascript') {
return 'javascript';
}
if (alias === 'text/css') {
return 'css';
}
console.log(alias);
throw new Error('Not implemented');
}
}
suite('Markdown - tokenization', () => {
var tokenizationSupport: Modes.ITokenizationSupport;
(function() {
let threadService = NULL_THREAD_SERVICE;
let modeService = new MarkdownMockModeService();
let services = new ServiceCollection();
services.set(IThreadService, threadService);
services.set(IModeService, modeService);
let inst = new InstantiationService(services);
threadService.setInstantiationService(inst);
let mode = new MarkdownMode(
{ id: 'markdown' },
inst,
threadService,
modeService,
null,
null
);
tokenizationSupport = mode.tokenizationSupport;
})();
test('', () => {
modesUtil.executeTests(tokenizationSupport, [
// HTML and embedded content - bug 16912
[{
line: '<b>foo</b>*bar*',
tokens: [
{ startIndex:0, type: htmlTokenTypes.getTag('b.md') },
{ startIndex:3, type: '' },
{ startIndex:6, type: htmlTokenTypes.getTag('b.md') },
{ startIndex:10, type: 'emphasis.md' }
]}],
[{
line: '</b>*bar*',
tokens: [
{ startIndex:0, type: htmlTokenTypes.getTag('b.md') },
{ startIndex:4, type: 'emphasis.md' }
]}],
[{
line: '<script>alert("foo")</script>*bar*',
tokens: [
{ startIndex:0, type: htmlTokenTypes.getTag('script.md') },
{ startIndex:8, type: 'mock-js' },
{ startIndex:20, type: htmlTokenTypes.getTag('script.md') },
{ startIndex:29, type: 'emphasis.md' }
]}],
[{
line: '<style>div { background: red }</style>*bar*',
tokens: [
{ startIndex:0, type: htmlTokenTypes.getTag('style.md') },
{ startIndex:7, type: 'mock-css' },
{ startIndex:30, type: htmlTokenTypes.getTag('style.md') },
{ startIndex:38, type: 'emphasis.md' }
]}]
]);
});
});
+24 -11
View File
@@ -3841,6 +3841,30 @@ declare module monaco.languages {
*/
export function registerCompletionItemProvider(languageId: string, provider: CompletionItemProvider): IDisposable;
/**
* Contains additional diagnostic information about the context in which
* a [code action](#CodeActionProvider.provideCodeActions) is run.
*/
export interface CodeActionContext {
/**
* An array of diagnostics.
*
* @readonly
*/
markers: editor.IMarkerData[];
}
/**
* The code action interface defines the contract between extensions and
* the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
*/
export interface CodeActionProvider {
/**
* Provide commands for the given document and range.
*/
provideCodeActions(model: editor.IReadOnlyModel, range: Range, context: CodeActionContext, token: CancellationToken): CodeAction[] | Thenable<CodeAction[]>;
}
/**
* Completion item kinds.
*/
@@ -4175,17 +4199,6 @@ declare module monaco.languages {
score: number;
}
/**
* The code action interface defines the contract between extensions and
* the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
*/
export interface CodeActionProvider {
/**
* Provide commands for the given document and range.
*/
provideCodeActions(model: editor.IReadOnlyModel, range: Range, token: CancellationToken): CodeAction[] | Thenable<CodeAction[]>;
}
/**
* Represents a parameter of a callable-signature. A parameter can
* have a label and a doc-comment.
@@ -8,6 +8,7 @@
import nls = require('vs/nls');
import { TPromise } from 'vs/base/common/winjs.base';
import Event from 'vs/base/common/event';
import { IPager } from 'vs/base/common/paging';
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
export interface IExtensionManifest {
@@ -18,70 +19,108 @@ export interface IExtensionManifest {
displayName?: string;
description?: string;
main?: string;
icon?: string;
}
export interface IGalleryVersion {
version: string;
date: string;
manifestUrl: string;
readmeUrl: string;
downloadUrl: string;
iconUrl: string;
downloadHeaders: { [key: string]: string; };
}
export interface IGalleryMetadata {
galleryApiUrl: string;
export interface IExtensionIdentity {
name: string;
publisher: string;
}
export interface IGalleryExtension {
id: string;
name: string;
displayName: string;
publisherId: string;
publisher: string;
publisherDisplayName: string;
description: string;
installCount: number;
rating: number;
ratingCount: number;
versions: IGalleryVersion[];
}
export interface IExtension extends IExtensionManifest {
galleryInformation?: IGalleryMetadata;
path?: string;
export interface IGalleryMetadata {
id: string;
publisherId: string;
publisherDisplayName: string;
}
export interface ILocalExtension {
id: string;
manifest: IExtensionManifest;
metadata: IGalleryMetadata;
path: string;
readmeUrl: string;
}
export const IExtensionManagementService = createDecorator<IExtensionManagementService>('extensionManagementService');
export const IExtensionGalleryService = createDecorator<IExtensionGalleryService>('extensionGalleryService');
export enum SortBy {
NoneOrRelevance = 0,
LastUpdatedDate = 1,
Title = 2,
PublisherName = 3,
InstallCount = 4,
PublishedDate = 5,
AverageRating = 6
}
export enum SortOrder {
Default = 0,
Ascending = 1,
Descending = 2
}
export interface IQueryOptions {
text?: string;
ids?: string[];
names?: string[];
pageSize?: number;
}
export interface IQueryResult {
firstPage: IExtension[];
total: number;
pageSize: number;
getPage(pageNumber: number): TPromise<IExtension[]>;
sortBy?: SortBy;
sortOrder?: SortOrder;
}
export interface IExtensionGalleryService {
serviceId: ServiceIdentifier<any>;
isEnabled(): boolean;
query(options?: IQueryOptions): TPromise<IQueryResult>;
query(options?: IQueryOptions): TPromise<IPager<IGalleryExtension>>;
}
export type InstallExtensionEvent = { id: string; gallery?: IGalleryExtension; };
export type DidInstallExtensionEvent = { id: string; local?: ILocalExtension; error?: Error; };
export interface IExtensionManagementService {
serviceId: ServiceIdentifier<any>;
onInstallExtension: Event<IExtensionManifest>;
onDidInstallExtension: Event<{ extension: IExtension; error?: Error; }>;
onUninstallExtension: Event<IExtension>;
onDidUninstallExtension: Event<IExtension>;
install(extension: IExtension): TPromise<IExtension>;
install(zipPath: string): TPromise<IExtension>;
uninstall(extension: IExtension): TPromise<void>;
getInstalled(includeDuplicateVersions?: boolean): TPromise<IExtension[]>;
onInstallExtension: Event<InstallExtensionEvent>;
onDidInstallExtension: Event<DidInstallExtensionEvent>;
onUninstallExtension: Event<string>;
onDidUninstallExtension: Event<string>;
install(extension: IGalleryExtension): TPromise<void>;
install(zipPath: string): TPromise<void>;
uninstall(extension: ILocalExtension): TPromise<void>;
getInstalled(includeDuplicateVersions?: boolean): TPromise<ILocalExtension[]>;
}
export const IExtensionTipsService = createDecorator<IExtensionTipsService>('extensionTipsService');
export interface IExtensionTipsService {
serviceId: ServiceIdentifier<IExtensionTipsService>;
getRecommendations(): TPromise<IExtension[]>;
getRecommendations(): TPromise<ILocalExtension[]>;
}
export const ExtensionsLabel = nls.localize('extensions', "Extensions");
@@ -7,7 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionManagementService, IExtension, IExtensionManifest } from './extensionManagement';
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent } from './extensionManagement';
import Event from 'vs/base/common/event';
export interface IExtensionManagementChannel extends IChannel {
@@ -15,9 +15,9 @@ export interface IExtensionManagementChannel extends IChannel {
call(command: 'event:onDidInstallExtension'): TPromise<void>;
call(command: 'event:onUninstallExtension'): TPromise<void>;
call(command: 'event:onDidUninstallExtension'): TPromise<void>;
call(command: 'install', extensionOrPath: IExtension | string): TPromise<IExtension>;
call(command: 'uninstall', extension: IExtension): TPromise<void>;
call(command: 'getInstalled', includeDuplicateVersions: boolean): TPromise<IExtension[]>;
call(command: 'install', extensionOrPath: ILocalExtension | string): TPromise<ILocalExtension>;
call(command: 'uninstall', extension: ILocalExtension): TPromise<void>;
call(command: 'getInstalled', includeDuplicateVersions: boolean): TPromise<ILocalExtension[]>;
call(command: string, arg: any): TPromise<any>;
}
@@ -44,29 +44,29 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
constructor(private channel: IExtensionManagementChannel) { }
private _onInstallExtension = eventFromCall(this.channel, 'event:onInstallExtension');
get onInstallExtension(): Event<IExtensionManifest> { return this._onInstallExtension; }
private _onInstallExtension = eventFromCall<InstallExtensionEvent>(this.channel, 'event:onInstallExtension');
get onInstallExtension(): Event<InstallExtensionEvent> { return this._onInstallExtension; }
private _onDidInstallExtension = eventFromCall(this.channel, 'event:onDidInstallExtension');
get onDidInstallExtension(): Event<{ extension: IExtension; error?: Error; }> { return this._onDidInstallExtension; }
private _onDidInstallExtension = eventFromCall<DidInstallExtensionEvent>(this.channel, 'event:onDidInstallExtension');
get onDidInstallExtension(): Event<DidInstallExtensionEvent> { return this._onDidInstallExtension; }
private _onUninstallExtension = eventFromCall(this.channel, 'event:onUninstallExtension');
get onUninstallExtension(): Event<IExtension> { return this._onUninstallExtension; }
private _onUninstallExtension = eventFromCall<string>(this.channel, 'event:onUninstallExtension');
get onUninstallExtension(): Event<string> { return this._onUninstallExtension; }
private _onDidUninstallExtension = eventFromCall(this.channel, 'event:onDidUninstallExtension');
get onDidUninstallExtension(): Event<IExtension> { return this._onDidUninstallExtension; }
private _onDidUninstallExtension = eventFromCall<string>(this.channel, 'event:onDidUninstallExtension');
get onDidUninstallExtension(): Event<string> { return this._onDidUninstallExtension; }
install(extension: IExtension): TPromise<IExtension>;
install(zipPath: string): TPromise<IExtension>;
install(arg: any): TPromise<IExtension> {
install(extension: IGalleryExtension): TPromise<void>;
install(zipPath: string): TPromise<void>;
install(arg: any): TPromise<void> {
return this.channel.call('install', arg);
}
uninstall(extension: IExtension): TPromise<void> {
uninstall(extension: ILocalExtension): TPromise<void> {
return this.channel.call('uninstall', extension);
}
getInstalled(includeDuplicateVersions?: boolean): TPromise<IExtension[]> {
getInstalled(includeDuplicateVersions?: boolean): TPromise<ILocalExtension[]> {
return this.channel.call('getInstalled', includeDuplicateVersions);
}
}
@@ -4,37 +4,37 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import { IExtension, IExtensionGalleryService, IGalleryVersion, IQueryOptions, IQueryResult } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IGalleryExtension, IExtensionGalleryService, IGalleryVersion, IQueryOptions, SortBy, SortOrder } from 'vs/platform/extensionManagement/common/extensionManagement';
import { isUndefined } from 'vs/base/common/types';
import { assign, getOrDefault } from 'vs/base/common/objects';
import { IRequestService } from 'vs/platform/request/common/request';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPager } from 'vs/base/common/paging';
import pkg from 'vs/platform/package';
import product from 'vs/platform/product';
export interface IGalleryExtensionFile {
interface IRawGalleryExtensionFile {
assetType: string;
}
export interface IGalleryExtensionVersion {
interface IRawGalleryExtensionVersion {
version: string;
lastUpdated: string;
assetUri: string;
files: IGalleryExtensionFile[];
files: IRawGalleryExtensionFile[];
}
export interface IGalleryExtension {
interface IRawGalleryExtension {
extensionId: string;
extensionName: string;
displayName: string;
shortDescription: string;
publisher: { displayName: string, publisherId: string, publisherName: string; };
versions: IGalleryExtensionVersion[];
galleryApiUrl: string;
statistics: IGalleryExtensionStatistics[];
versions: IRawGalleryExtensionVersion[];
statistics: IRawGalleryExtensionStatistics[];
}
export interface IGalleryExtensionStatistics {
interface IRawGalleryExtensionStatistics {
statisticName: string;
value: number;
}
@@ -63,22 +63,6 @@ enum FilterType {
SearchText = 10
}
enum SortBy {
NoneOrRelevance = 0,
LastUpdatedDate = 1,
Title = 2,
PublisherName = 3,
InstallCount = 4,
PublishedDate = 5,
AverageRating = 6
}
enum SortOrder {
Default = 0,
Ascending = 1,
Descending = 2
}
interface ICriterium {
filterType: FilterType;
value?: string;
@@ -130,8 +114,12 @@ class Query {
return new Query(assign({}, this.state, { criteria }));
}
withSort(sortBy: SortBy, sortOrder = SortOrder.Default): Query {
return new Query(assign({}, this.state, { sortBy, sortOrder }));
withSortBy(sortBy: SortBy): Query {
return new Query(assign({}, this.state, { sortBy }));
}
withSortOrder(sortOrder): Query {
return new Query(assign({}, this.state, { sortOrder }));
}
withFlags(...flags: Flags[]): Query {
@@ -152,39 +140,34 @@ class Query {
}
}
function getInstallCount(statistics: IGalleryExtensionStatistics[]): number {
if (!statistics) {
return 0;
}
const result = statistics.filter(s => s.statisticName === 'install')[0];
function getStatistic(statistics: IRawGalleryExtensionStatistics[], name: string): number {
const result = (statistics || []).filter(s => s.statisticName === name)[0];
return result ? result.value : 0;
}
function toExtension(galleryExtension: IGalleryExtension, extensionsGalleryUrl: string, downloadHeaders: any): IExtension {
function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string, downloadHeaders: any): IGalleryExtension {
const versions = galleryExtension.versions.map<IGalleryVersion>(v => ({
version: v.version,
date: v.lastUpdated,
downloadHeaders,
downloadUrl: `${ v.assetUri }/Microsoft.VisualStudio.Services.VSIXPackage?install=true`,
manifestUrl: `${ v.assetUri }/Microsoft.VisualStudio.Code.Manifest`
manifestUrl: `${ v.assetUri }/Microsoft.VisualStudio.Code.Manifest`,
readmeUrl: `${ v.assetUri }/Microsoft.VisualStudio.Services.Content.Details`,
iconUrl: `${ v.assetUri }/Microsoft.VisualStudio.Services.Icons.Default`
}));
return {
id: galleryExtension.extensionId,
name: galleryExtension.extensionName,
displayName: galleryExtension.displayName || galleryExtension.extensionName,
displayName: galleryExtension.displayName,
publisherId: galleryExtension.publisher.publisherId,
publisher: galleryExtension.publisher.publisherName,
version: versions[0].version,
engines: { vscode: void 0 }, // TODO: ugly
publisherDisplayName: galleryExtension.publisher.displayName,
description: galleryExtension.shortDescription || '',
galleryInformation: {
galleryApiUrl: extensionsGalleryUrl,
id: galleryExtension.extensionId,
publisherId: galleryExtension.publisher.publisherId,
publisherDisplayName: galleryExtension.publisher.displayName,
installCount: getInstallCount(galleryExtension.statistics),
versions
}
installCount: getStatistic(galleryExtension.statistics, 'install'),
rating: getStatistic(galleryExtension.statistics, 'averagerating'),
ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'),
versions
};
}
@@ -212,12 +195,12 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return !!this.extensionsGalleryUrl;
}
query(options: IQueryOptions = {}): TPromise<IQueryResult> {
query(options: IQueryOptions = {}): TPromise<IPager<IGalleryExtension>> {
if (!this.isEnabled()) {
return TPromise.wrapError(new Error('No extension gallery service configured.'));
}
const type = options.ids ? 'ids' : (options.text ? 'text' : 'all');
const type = options.names ? 'ids' : (options.text ? 'text' : 'all');
const text = options.text || '';
const pageSize = getOrDefault(options, o => o.pageSize, 50);
@@ -229,14 +212,21 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code');
if (text) {
query = query.withFilter(FilterType.SearchText, text)
.withSort(SortBy.NoneOrRelevance);
query = query.withFilter(FilterType.SearchText, text).withSortBy(SortBy.NoneOrRelevance);
} else if (options.ids) {
options.ids.forEach(id => {
query = query.withFilter(FilterType.ExtensionName, id);
});
query = options.ids.reduce((query, id) => query.withFilter(FilterType.ExtensionId, id), query);
} else if (options.names) {
query = options.names.reduce((query, name) => query.withFilter(FilterType.ExtensionName, name), query);
} else {
query = query.withSort(SortBy.InstallCount);
query = query.withSortBy(SortBy.InstallCount);
}
if (typeof options.sortBy === 'number') {
query = query.withSortBy(options.sortBy);
}
if (typeof options.sortOrder === 'number') {
query = query.withSortOrder(options.sortOrder);
}
return this.queryGallery(query).then(({ galleryExtensions, total }) => {
@@ -251,7 +241,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
});
}
private queryGallery(query: Query): TPromise<{ galleryExtensions: IGalleryExtension[], total: number; }> {
private queryGallery(query: Query): TPromise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> {
const data = JSON.stringify(query.raw);
return this.getRequestHeaders()
@@ -15,8 +15,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { flatten } from 'vs/base/common/arrays';
import { extract, buffer } from 'vs/base/node/zip';
import { Promise, TPromise } from 'vs/base/common/winjs.base';
import { IExtensionManagementService, IExtension, IExtensionManifest, IGalleryMetadata, IGalleryVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { extensionEquals, getTelemetryData } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IExtensionIdentity, IExtensionManifest, IGalleryVersion, IGalleryMetadata, InstallExtensionEvent, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { download, json, IRequestOptions } from 'vs/base/node/request';
import { getProxyAgent } from 'vs/base/node/proxy';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -29,20 +28,23 @@ import { groupBy, values } from 'vs/base/common/collections';
import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator';
import pkg from 'vs/platform/package';
function parseManifest(raw: string): TPromise<IExtensionManifest> {
function parseManifest(raw: string): TPromise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> {
return new Promise((c, e) => {
try {
c(JSON.parse(raw));
const manifest = JSON.parse(raw);
const metadata = manifest.__metadata || null;
delete manifest.__metadata;
c({ manifest, metadata });
} catch (err) {
e(new Error(nls.localize('invalidManifest', "Extension invalid: package.json is not a JSON file.")));
}
});
}
function validate(zipPath: string, extension?: IExtension, version = extension && extension.version): TPromise<IExtension> {
function validate(zipPath: string, extension?: IExtensionIdentity, version?: string): TPromise<IExtensionManifest> {
return buffer(zipPath, 'extension/package.json')
.then(buffer => parseManifest(buffer.toString('utf8')))
.then(manifest => {
.then(({ manifest }) => {
if (extension) {
if (extension.name !== manifest.name) {
return Promise.wrapError(Error(nls.localize('invalidName', "Extension invalid: manifest name mismatch.")));
@@ -61,28 +63,7 @@ function validate(zipPath: string, extension?: IExtension, version = extension &
});
}
function createExtension(manifest: IExtensionManifest, galleryInformation?: IGalleryMetadata, path?: string): IExtension {
const extension: IExtension = {
name: manifest.name,
displayName: manifest.displayName || manifest.name,
publisher: manifest.publisher,
version: manifest.version,
engines: { vscode: manifest.engines.vscode },
description: manifest.description || ''
};
if (galleryInformation) {
extension.galleryInformation = galleryInformation;
}
if (path) {
extension.path = path;
}
return extension;
}
function getExtensionId(extension: IExtensionManifest, version = extension.version): string {
function getExtensionId(extension: IExtensionIdentity, version: string): string {
return `${ extension.publisher }.${ extension.name }-${ version }`;
}
@@ -95,17 +76,17 @@ export class ExtensionManagementService implements IExtensionManagementService {
private obsoleteFileLimiter: Limiter<void>;
private disposables: IDisposable[];
private _onInstallExtension = new Emitter<IExtensionManifest>();
onInstallExtension: Event<IExtensionManifest> = this._onInstallExtension.event;
private _onInstallExtension = new Emitter<InstallExtensionEvent>();
onInstallExtension: Event<InstallExtensionEvent> = this._onInstallExtension.event;
private _onDidInstallExtension = new Emitter<{ extension: IExtension; isUpdate: boolean; error?: Error; }>();
onDidInstallExtension: Event<{ extension: IExtension; isUpdate: boolean; error?: Error; }> = this._onDidInstallExtension.event;
private _onDidInstallExtension = new Emitter<DidInstallExtensionEvent>();
onDidInstallExtension: Event<DidInstallExtensionEvent> = this._onDidInstallExtension.event;
private _onUninstallExtension = new Emitter<IExtension>();
onUninstallExtension: Event<IExtension> = this._onUninstallExtension.event;
private _onUninstallExtension = new Emitter<string>();
onUninstallExtension: Event<string> = this._onUninstallExtension.event;
private _onDidUninstallExtension = new Emitter<IExtension>();
onDidUninstallExtension: Event<IExtension> = this._onDidUninstallExtension.event;
private _onDidUninstallExtension = new Emitter<string>();
onDidUninstallExtension: Event<string> = this._onDidUninstallExtension.event;
constructor(
@IEnvironmentService private environmentService: IEnvironmentService,
@@ -115,71 +96,73 @@ export class ExtensionManagementService implements IExtensionManagementService {
this.obsoletePath = path.join(this.extensionsPath, '.obsolete');
this.obsoleteFileLimiter = new Limiter(1);
this.disposables = [
this.onDidInstallExtension(({ extension, isUpdate, error }) => telemetryService.publicLog(
isUpdate ? 'extensionGallery2:update' : 'extensionGallery2:install',
assign(getTelemetryData(extension), { success: !error })
)),
this.onDidUninstallExtension(extension => telemetryService.publicLog(
'extensionGallery2:uninstall',
assign(getTelemetryData(extension), { success: true })
))
];
// this.disposables = [
// this.onDidInstallExtension(({ extension, isUpdate, error }) => telemetryService.publicLog(
// isUpdate ? 'extensionGallery2:update' : 'extensionGallery2:install',
// assign(getTelemetryData(extension), { success: !error })
// )),
// this.onDidUninstallExtension(extension => telemetryService.publicLog(
// 'extensionGallery2:uninstall',
// assign(getTelemetryData(extension), { success: true })
// ))
// ];
}
install(extension: IExtension): TPromise<IExtension>;
install(zipPath: string): TPromise<IExtension>;
install(arg: any): TPromise<IExtension> {
install(extension: IGalleryExtension): TPromise<void>;
install(zipPath: string): TPromise<void>;
install(arg: any): TPromise<void> {
if (types.isString(arg)) {
return this.installFromZip(arg);
const zipPath = arg as string;
return validate(zipPath).then(manifest => {
const id = getExtensionId(manifest, manifest.version);
this._onInstallExtension.fire({ id });
return this.installValidExtension(zipPath, id);
});
}
const extension = arg as IExtension;
return this.isObsolete(extension).then(obsolete => {
const extension = arg as IGalleryExtension;
const id = getExtensionId(extension, extension.versions[0].version);
this._onInstallExtension.fire({ id, gallery: extension });
return this.isObsolete(id).then(obsolete => {
if (obsolete) {
return TPromise.wrapError(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", extension.name)));
return TPromise.wrapError<void>(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", extension.displayName || extension.name)));
}
return this.installFromGallery(arg);
});
}
private installFromGallery(extension: IExtension): TPromise<IExtension> {
const galleryInformation = extension.galleryInformation;
private installFromGallery(extension: IGalleryExtension): TPromise<void> {
return this.getLastValidExtensionVersion(extension).then(versionInfo => {
const version = versionInfo.version;
const url = versionInfo.downloadUrl;
const headers = versionInfo.downloadHeaders;
const zipPath = path.join(tmpdir(), extension.id);
const id = getExtensionId(extension, version);
const metadata = {
id: extension.id,
publisherId: extension.publisherId,
publisherDisplayName: extension.publisherDisplayName
};
if (!galleryInformation) {
return TPromise.wrapError(new Error(nls.localize('missingGalleryInformation', "Gallery information is missing")));
}
this._onInstallExtension.fire(extension);
return this.getLastValidExtensionVersion(extension, extension.galleryInformation.versions).then(versionInfo => {
return this.getInstalled()
.then(installed => installed.some(e => extensionEquals(e, extension)))
.then(isUpdate => {
const version = versionInfo.version;
const url = versionInfo.downloadUrl;
const headers = versionInfo.downloadHeaders;
const zipPath = path.join(tmpdir(), galleryInformation.id);
const extensionPath = path.join(this.extensionsPath, getExtensionId(extension, version));
const manifestPath = path.join(extensionPath, 'package.json');
return this.request(url)
.then(opts => assign(opts, { headers }))
.then(opts => download(zipPath, opts))
.then(() => validate(zipPath, extension, version))
.then(manifest => extract(zipPath, extensionPath, { sourcePath: 'extension', overwrite: true }).then(() => manifest))
.then(manifest => assign({ __metadata: galleryInformation }, manifest))
.then(manifest => pfs.writeFile(manifestPath, JSON.stringify(manifest, null, '\t')))
.then(() => { this._onDidInstallExtension.fire({ extension, isUpdate }); return extension; })
.then(null, error => { this._onDidInstallExtension.fire({ extension, isUpdate, error }); return TPromise.wrapError(error); });
});
return this.request(url)
.then(opts => assign(opts, { headers }))
.then(opts => download(zipPath, opts))
.then(() => validate(zipPath, extension, version))
.then(() => this.installValidExtension(zipPath, id, metadata));
});
}
private getLastValidExtensionVersion(extension: IExtension, versions: IGalleryVersion[]): TPromise<IGalleryVersion> {
private getLastValidExtensionVersion(extension: IGalleryExtension): TPromise<IGalleryVersion> {
return this._getLastValidExtensionVersion(extension, extension.versions);
}
private _getLastValidExtensionVersion(extension: IGalleryExtension, versions: IGalleryVersion[]): TPromise<IGalleryVersion> {
if (!versions.length) {
return TPromise.wrapError(new Error(nls.localize('noCompatible', "Couldn't find a compatible version of {0} with this version of Code.", extension.displayName)));
return TPromise.wrapError(new Error(nls.localize('noCompatible', "Couldn't find a compatible version of {0} with this version of Code.", extension.displayName || extension.name)));
}
const version = versions[0];
@@ -193,42 +176,49 @@ export class ExtensionManagementService implements IExtensionManagementService {
};
if (!isValidExtensionVersion(pkg.version, desc, [])) {
return this.getLastValidExtensionVersion(extension, versions.slice(1));
return this._getLastValidExtensionVersion(extension, versions.slice(1));
}
return version;
});
}
private installFromZip(zipPath: string): TPromise<IExtension> {
return validate(zipPath).then(manifest => {
const extensionPath = path.join(this.extensionsPath, getExtensionId(manifest));
this._onInstallExtension.fire(manifest);
private installValidExtension(zipPath: string, id: string, metadata: IGalleryMetadata = null): TPromise<void> {
const extensionPath = path.join(this.extensionsPath, id);
const manifestPath = path.join(extensionPath, 'package.json');
return this.getInstalled()
.then(installed => installed.some(e => extensionEquals(e, manifest)))
.then(isUpdate => {
return extract(zipPath, extensionPath, { sourcePath: 'extension', overwrite: true })
.then(() => pfs.readFile(manifestPath, 'utf8'))
.then(raw => parseManifest(raw))
.then(({ manifest }) => {
return pfs.readdir(extensionPath).then(children => {
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0];
const readmeUrl = readme ? `file://${ extensionPath }/${ readme }` : null;
return extract(zipPath, extensionPath, { sourcePath: 'extension', overwrite: true })
.then(() => createExtension(manifest, (<any> manifest).__metadata, extensionPath))
.then(extension => { this._onDidInstallExtension.fire({ extension, isUpdate }); return extension; });
});
});
const local: ILocalExtension = { id, manifest, metadata, path: extensionPath, readmeUrl };
const rawManifest = assign(manifest, { __metadata: metadata });
return pfs.writeFile(manifestPath, JSON.stringify(rawManifest, null, '\t'))
.then(() => this._onDidInstallExtension.fire({ id, local }));
});
})
.then<void>(null, error => { this._onDidInstallExtension.fire({ id, error }); return TPromise.wrapError(error); });
}
uninstall(extension: IExtension): TPromise<void> {
const extensionPath = extension.path || path.join(this.extensionsPath, getExtensionId(extension));
uninstall(extension: ILocalExtension): TPromise<void> {
const id = extension.id;
const extensionPath = path.join(this.extensionsPath, id);
return pfs.exists(extensionPath)
.then(exists => exists ? null : Promise.wrapError(new Error(nls.localize('notExists', "Could not find extension"))))
.then(() => this._onUninstallExtension.fire(extension))
.then(() => this.setObsolete(extension))
.then(() => this._onUninstallExtension.fire(id))
.then(() => this.setObsolete(id))
.then(() => pfs.rimraf(extensionPath))
.then(() => this.unsetObsolete(extension))
.then(() => this._onDidUninstallExtension.fire(extension));
.then(() => this.unsetObsolete(id))
.then(() => this._onDidUninstallExtension.fire(id));
}
getInstalled(includeDuplicateVersions: boolean = false): TPromise<IExtension[]> {
getInstalled(includeDuplicateVersions: boolean = false): TPromise<ILocalExtension[]> {
const all = this.getAllInstalled();
if (includeDuplicateVersions) {
@@ -236,35 +226,39 @@ export class ExtensionManagementService implements IExtensionManagementService {
}
return all.then(extensions => {
const byId = values(groupBy(extensions, p => `${ p.publisher }.${ p.name }`));
return byId.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0]);
const byId = values(groupBy(extensions, p => `${ p.manifest.publisher }.${ p.manifest.name }`));
return byId.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]);
});
}
private getAllInstalled(): TPromise<IExtension[]> {
private getAllInstalled(): TPromise<ILocalExtension[]> {
const limiter = new Limiter(10);
return this.getObsoleteExtensions()
.then(obsolete => {
return pfs.readdir(this.extensionsPath)
.then(extensions => extensions.filter(e => !obsolete[e]))
.then<IExtension[]>(extensions => Promise.join(extensions.map(e => {
const extensionPath = path.join(this.extensionsPath, e);
.then(extensions => extensions.filter(id => !obsolete[id]))
.then<ILocalExtension[]>(extensionIds => Promise.join(extensionIds.map(id => {
const extensionPath = path.join(this.extensionsPath, id);
return limiter.queue(
() => pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8')
const each = () => pfs.readdir(extensionPath).then(children => {
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0];
const readmeUrl = readme ? `file://${ extensionPath }/${ readme }` : null;
return pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8')
.then(raw => parseManifest(raw))
.then(manifest => createExtension(manifest, (<any> manifest).__metadata, extensionPath))
.then(null, () => null)
);
.then<ILocalExtension>(({ manifest, metadata }) => ({ id, manifest, metadata, path: extensionPath, readmeUrl }));
}).then(null, () => null);
return limiter.queue(each);
})))
.then(result => result.filter(a => !!a));
});
}
removeDeprecatedExtensions(): TPromise<void> {
const outdated = this.getOutdatedExtensions()
.then(extensions => extensions.map(e => getExtensionId(e)));
const outdated = this.getOutdatedExtensionIds()
.then(extensions => extensions.map(e => getExtensionId(e.manifest, e.manifest.version)));
const obsolete = this.getObsoleteExtensions()
.then(obsolete => Object.keys(obsolete));
@@ -279,28 +273,21 @@ export class ExtensionManagementService implements IExtensionManagementService {
});
}
private getOutdatedExtensions(): TPromise<IExtension[]> {
return this.getAllInstalled().then(plugins => {
const byId = values(groupBy(plugins, p => `${ p.publisher }.${ p.name }`));
const extensions = flatten(byId.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version)).slice(1)));
return extensions
.filter(e => !!e.path);
});
private getOutdatedExtensionIds(): TPromise<ILocalExtension[]> {
return this.getAllInstalled()
.then(extensions => values(groupBy(extensions, p => `${ p.manifest.publisher }.${ p.manifest.name }`)))
.then(versions => flatten(versions.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1))));
}
private isObsolete(extension: IExtension): TPromise<boolean> {
const id = getExtensionId(extension);
private isObsolete(id: string): TPromise<boolean> {
return this.withObsoleteExtensions(obsolete => !!obsolete[id]);
}
private setObsolete(extension: IExtension): TPromise<void> {
const id = getExtensionId(extension);
private setObsolete(id: string): TPromise<void> {
return this.withObsoleteExtensions(obsolete => assign(obsolete, { [id]: true }));
}
private unsetObsolete(extension: IExtension): TPromise<void> {
const id = getExtensionId(extension);
private unsetObsolete(id: string): TPromise<void> {
return this.withObsoleteExtensions<void>(obsolete => delete obsolete[id]);
}
@@ -5,48 +5,62 @@
'use strict';
import { IExtension, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionIdentity, ILocalExtension, IGalleryExtension, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { TPromise } from 'vs/base/common/winjs.base';
import * as semver from 'semver';
export function getExtensionId(extension: IExtension): string {
return `${ extension.publisher }.${ extension.name }`;
}
export function extensionEquals(one: IExtension, other: IExtension): boolean {
export function extensionEquals(one: IExtensionIdentity, other: IExtensionIdentity): boolean {
return one.publisher === other.publisher && one.name === other.name;
}
export function getTelemetryData(extension: IExtension): any {
return {
id: getExtensionId(extension),
name: extension.name,
galleryId: extension.galleryInformation ? extension.galleryInformation.id : null,
publisherId: extension.galleryInformation ? extension.galleryInformation.publisherId : null,
publisherName: extension.publisher,
publisherDisplayName: extension.galleryInformation ? extension.galleryInformation.publisherDisplayName : null
};
export function getTelemetryData(extension: ILocalExtension | IGalleryExtension): any {
const local = extension as ILocalExtension;
const gallery = extension as IGalleryExtension;
if (local.path) {
return {
id: `${ local.manifest.publisher }.${ local.manifest.name }`,
name: local.manifest.name,
galleryId: local.metadata ? local.metadata.id : null,
publisherId: local.metadata ? local.metadata.publisherId : null,
publisherName: local.manifest.publisher,
publisherDisplayName: local.metadata ? local.metadata.publisherDisplayName : null
};
} else {
return {
id: `${ gallery.publisher }.${ gallery.name }`,
name: gallery.name,
galleryId: gallery.id,
publisherId: gallery.publisherId,
publisherName: gallery.publisher,
publisherDisplayName: gallery.publisherDisplayName
};
}
}
export function getOutdatedExtensions(extensionsService: IExtensionManagementService, galleryService: IExtensionGalleryService): TPromise<IExtension[]> {
export function getOutdatedExtensions(extensionsService: IExtensionManagementService, galleryService: IExtensionGalleryService): TPromise<ILocalExtension[]> {
if (!galleryService.isEnabled()) {
return TPromise.as([]);
}
return extensionsService.getInstalled().then(installed => {
const ids = installed.map(getExtensionId);
const names = installed.map(({ manifest }) => `${ manifest.publisher }.${ manifest.name }`);
if (installed.length === 0) {
return TPromise.as([]);
}
return galleryService.query({ ids, pageSize: ids.length }).then(result => {
return galleryService.query({ names, pageSize: names.length }).then(result => {
const available = result.firstPage;
return available.filter(extension => {
const local = installed.filter(local => extensionEquals(local, extension))[0];
return local && semver.lt(local.version, extension.version);
});
return available.map(extension => {
const local = installed.filter(local => extensionEquals(local.manifest, extension))[0];
if (local && semver.lt(local.manifest.version, extension.versions[0].version)) {
return local;
} else {
return null;
}
}).filter(e => !!e);
});
});
}
+1 -1
View File
@@ -2630,7 +2630,7 @@ declare namespace vscode {
/**
* A diagnostics collection is a container that manages a set of
* [diagnostics](#Diagnostic). Diagnostics are always scopes to a
* a diagnostics collection and a resource.
* diagnostics collection and a resource.
*
* To get an instance of a `DiagnosticCollection` use
* [createDiagnosticCollection](#languages.createDiagnosticCollection).
@@ -12,7 +12,7 @@ import {Builder, $} from 'vs/base/browser/builder';
import {Action} from 'vs/base/common/actions';
import errors = require('vs/base/common/errors');
import {ActionsOrientation, ActionBar, IActionItem} from 'vs/base/browser/ui/actionbar/actionbar';
import {ToolBar} from 'vs/base/browser/ui/toolbar/toolbar';
import {CONTEXT, ToolBar} from 'vs/base/browser/ui/toolbar/toolbar';
import {Registry} from 'vs/platform/platform';
import {CompositeEvent, EventType} from 'vs/workbench/common/events';
import {ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions} from 'vs/workbench/browser/viewlet';
@@ -27,10 +27,15 @@ import {IInstantiationService} from 'vs/platform/instantiation/common/instantiat
import {IMessageService} from 'vs/platform/message/common/message';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService';
import {Scope, IActionBarRegistry, Extensions as ActionBarExtensions, prepareActions} from 'vs/workbench/browser/actionBarRegistry';
import Severity from 'vs/base/common/severity';
import {IAction} from 'vs/base/common/actions';
import events = require('vs/base/common/events');
export class ActivitybarPart extends Part implements IActivityService {
public serviceId = IActivityService;
private viewletSwitcherBar: ActionBar;
private globalViewletSwitcherBar: ActionBar;
private globalToolBar: ToolBar;
private activityActionItems: { [actionId: string]: IActionItem; };
private viewletIdToActions: { [viewletId: string]: ActivityAction; };
@@ -103,7 +108,7 @@ export class ActivitybarPart extends Part implements IActivityService {
this.createViewletSwitcher($result.clone());
// Bottom Toolbar with action items for global actions
// this.createGlobalToolBarArea($result.clone()); // not used currently
this.createGlobalToolBarArea($result.clone()); // not used currently
return $result;
}
@@ -118,106 +123,125 @@ export class ActivitybarPart extends Part implements IActivityService {
});
this.viewletSwitcherBar.getContainer().addClass('position-top');
// Build Viewlet Actions in correct order
let activeViewlet = this.viewletService.getActiveViewlet();
let registry = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets));
let viewletActions: Action[] = registry.getViewlets()
.sort((v1: ViewletDescriptor, v2: ViewletDescriptor) => v1.order - v2.order)
.map((viewlet: ViewletDescriptor) => {
let action = this.instantiationService.createInstance(ViewletActivityAction, viewlet.id + '.activity-bar-action', viewlet);
// Global viewlet switcher is right below
this.globalViewletSwitcherBar = new ActionBar(div, {
actionItemProvider: (action: Action) => this.activityActionItems[action.id],
orientation: ActionsOrientation.VERTICAL,
ariaLabel: nls.localize('globalActivityBarAriaLabel', "Active Global View Switcher")
});
this.globalViewletSwitcherBar.getContainer().addClass('position-bottom');
// Build Viewlet Actions in correct order
const activeViewlet = this.viewletService.getActiveViewlet();
const registry = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets));
const allViewletActions = registry.getViewlets();
const actionOptions = { label: true, icon: true };
const toAction = (viewlet: ViewletDescriptor) => {
let action = this.instantiationService.createInstance(ViewletActivityAction, viewlet.id + '.activity-bar-action', viewlet);
let keybinding: string = null;
let keys = this.keybindingService.lookupKeybindings(viewlet.id).map(k => this.keybindingService.getLabelFor(k));
if (keys && keys.length) {
keybinding = keys[0];
}
this.activityActionItems[action.id] = new ActivityActionItem(action, viewlet.name, keybinding);
this.viewletIdToActions[viewlet.id] = action;
// Mark active viewlet action as active
if (activeViewlet && activeViewlet.getId() === viewlet.id) {
action.activate();
}
return action;
};
// Add to viewlet switcher
this.viewletSwitcherBar.push(allViewletActions
.filter(v => !v.isGlobal)
.sort((v1, v2) => v1.order - v2.order)
.map(toAction)
, actionOptions);
// Add to viewlet switcher
this.globalViewletSwitcherBar.push(allViewletActions
.filter(v => v.isGlobal)
.sort((v1, v2) => v1.order - v2.order)
.map(toAction),
actionOptions);
}
private createGlobalToolBarArea(div: Builder): void {
// Global action bar is on the bottom
this.globalToolBar = new ToolBar(div.getHTMLElement(), this.contextMenuService, {
actionItemProvider: (action: Action) => this.activityActionItems[action.id],
orientation: ActionsOrientation.VERTICAL
});
this.globalToolBar.getContainer().addClass('global');
this.globalToolBar.actionRunner.addListener2(events.EventType.RUN, (e: any) => {
// Check for Error
if (e.error && !errors.isPromiseCanceledError(e.error)) {
this.messageService.show(Severity.Error, e.error);
}
// Log in telemetry
if (this.telemetryService) {
this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: 'activityBar' });
}
});
// Build Global Actions in correct order
let primaryActions = this.getGlobalActions(true);
let secondaryActions = this.getGlobalActions(false);
if (primaryActions.length + secondaryActions.length > 0) {
this.globalToolBar.getContainer().addClass('position-bottom');
}
// Add to global action bar
this.globalToolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions))();
}
private getGlobalActions(primary: boolean): IAction[] {
let actionBarRegistry = <IActionBarRegistry>Registry.as(ActionBarExtensions.Actionbar);
// Collect actions from actionbar contributor
let actions: IAction[];
if (primary) {
actions = actionBarRegistry.getActionBarActionsForContext(Scope.GLOBAL, CONTEXT);
} else {
actions = actionBarRegistry.getSecondaryActionBarActionsForContext(Scope.GLOBAL, CONTEXT);
}
return actions.map((action: Action) => {
if (primary) {
let keybinding: string = null;
let keys = this.keybindingService.lookupKeybindings(viewlet.id).map(k => this.keybindingService.getLabelFor(k));
let keys = this.keybindingService.lookupKeybindings(action.id).map(k => this.keybindingService.getLabelFor(k));
if (keys && keys.length) {
keybinding = keys[0];
}
this.activityActionItems[action.id] = new ActivityActionItem(action, viewlet.name, keybinding);
this.viewletIdToActions[viewlet.id] = action;
let actionItem = actionBarRegistry.getActionItemForContext(Scope.GLOBAL, CONTEXT, action);
// Mark active viewlet action as active
if (activeViewlet && activeViewlet.getId() === viewlet.id) {
action.activate();
if (!actionItem) {
actionItem = new ActivityActionItem(action, action.label, keybinding);
}
return action;
if (actionItem instanceof ActivityActionItem) {
(<ActivityActionItem> actionItem).keybinding = keybinding;
}
this.activityActionItems[action.id] = actionItem;
}
);
// Add to viewlet switcher
this.viewletSwitcherBar.push(viewletActions, { label: true, icon: true });
return action;
});
}
// private createGlobalToolBarArea(div: Builder): void {
// // Global action bar is on the bottom
// this.globalToolBar = new ToolBar(div.getHTMLElement(), this.contextMenuService, {
// actionItemProvider: (action: Action) => this.activityActionItems[action.id],
// orientation: ActionsOrientation.VERTICAL
// });
// this.globalToolBar.getContainer().addClass('global');
// this.globalToolBar.actionRunner.addListener(events.EventType.RUN, (e: any) => {
// // Check for Error
// if (e.error && !errors.isPromiseCanceledError(e.error)) {
// this.messageService.show(Severity.Error, e.error);
// }
// // Log in telemetry
// if (this.telemetryService) {
// this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: 'activityBar' });
// }
// });
// // Build Global Actions in correct order
// let primaryActions = this.getGlobalActions(true);
// let secondaryActions = this.getGlobalActions(false);
// if (primaryActions.length + secondaryActions.length > 0) {
// this.globalToolBar.getContainer().addClass('position-bottom');
// }
// // Add to global action bar
// this.globalToolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions))();
// }
// private getGlobalActions(primary: boolean): IAction[] {
// let actionBarRegistry = <IActionBarRegistry>Registry.as(ActionBarExtensions.Actionbar);
// // Collect actions from actionbar contributor
// let actions: IAction[];
// if (primary) {
// actions = actionBarRegistry.getActionBarActionsForContext(Scope.GLOBAL, CONTEXT);
// } else {
// actions = actionBarRegistry.getSecondaryActionBarActionsForContext(Scope.GLOBAL, CONTEXT);
// }
// return actions.map((action: Action) => {
// if (primary) {
// let keybinding: string = null;
// let keys = this.keybindingService.lookupKeybindings(action.id).map(k => this.keybindingService.getLabelFor(k));
// if (keys && keys.length) {
// keybinding = keys[0];
// }
// let actionItem = actionBarRegistry.getActionItemForContext(Scope.GLOBAL, CONTEXT, action);
// if (!actionItem) {
// actionItem = new ActivityActionItem(action, action.label, keybinding);
// }
// if (actionItem instanceof ActivityActionItem) {
// (<ActivityActionItem> actionItem).keybinding = keybinding;
// }
// this.activityActionItems[action.id] = actionItem;
// }
// return action;
// });
// }
public dispose(): void {
if (this.viewletSwitcherBar) {
this.viewletSwitcherBar.dispose();
@@ -32,9 +32,9 @@ import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {EditorStacksModel} from 'vs/workbench/common/editor/editorStacksModel';
import {CloseEditorsInGroupAction, CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, KeepEditorAction, CloseOtherEditorsInGroupAction, OpenToSideAction,
NavigateBetweenGroupsAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInLeftGroupAction,
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, ReopenClosedEditorAction, OpenPreviousEditorInGroupAction, NAVIGATE_IN_LEFT_GROUP_PREFIX,
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, NAVIGATE_IN_LEFT_GROUP_PREFIX,
GlobalQuickOpenAction, OpenPreviousEditorFromHistoryAction, QuickOpenNavigateNextAction, QuickOpenNavigatePreviousAction, ShowAllEditorsAction, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, ClearEditorHistoryAction, ShowEditorsInCenterGroupAction,
NAVIGATE_IN_CENTER_GROUP_PREFIX, ShowEditorsInRightGroupAction, NAVIGATE_IN_RIGHT_GROUP_PREFIX, RemoveFromEditorHistoryAction, FocusLastEditorInStackAction
NAVIGATE_IN_CENTER_GROUP_PREFIX, ShowEditorsInRightGroupAction, NAVIGATE_IN_RIGHT_GROUP_PREFIX, RemoveFromEditorHistoryAction, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction
} from 'vs/workbench/browser/parts/editor/editorActions';
// Register String Editor
@@ -263,12 +263,18 @@ function navigateKeybinding(shift: boolean): IKeybindings {
// Register Editor Actions
const category = nls.localize('view', "View");
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorInGroupAction, OpenPreviousEditorInGroupAction.ID, OpenPreviousEditorInGroupAction.LABEL, {
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.Tab,
mac: {
primary: KeyMod.WinCtrl | KeyCode.Tab
}
}), 'Open Previous in Editor Group');
}), 'Open Next Recently Used Editor in Group');
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyMod.Shift |  KeyCode.Tab,
mac: {
primary: KeyMod.WinCtrl | KeyMod.Shift |  KeyCode.Tab
}
}), 'Open Previous Recently Used Editor in Group');
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: null, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInLeftGroupAction, ShowEditorsInLeftGroupAction.ID, ShowEditorsInLeftGroupAction.LABEL), 'View: Show Editors in Left Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInCenterGroupAction, ShowEditorsInCenterGroupAction.ID, ShowEditorsInCenterGroupAction.LABEL), 'View: Show Editors in Center Group', category);
@@ -1014,10 +1014,7 @@ export class ShowAllEditorsAction extends QuickOpenAction {
}
}
export class OpenPreviousEditorInGroupAction extends Action {
public static ID = 'workbench.action.openPreviousEditorInGroup';
public static LABEL = nls.localize('openPreviousEditorInGroup', "Open Previous in Editor Group");
export class BaseQuickOpenEditorInGroupAction extends Action {
constructor(
id: string,
@@ -1052,6 +1049,40 @@ export class OpenPreviousEditorInGroupAction extends Action {
}
}
export class OpenPreviousRecentlyUsedEditorInGroupAction extends BaseQuickOpenEditorInGroupAction {
public static ID = 'workbench.action.openPreviousRecentlyUsedEditorInGroup';
public static LABEL = nls.localize('openPreviousEditorInGroup', "Open Previous Recently Used Editor in Group");
constructor(
id: string,
label: string,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IKeybindingService keybindingService: IKeybindingService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService
) {
super(id, label, quickOpenService, keybindingService, editorGroupService, editorService);
}
}
export class OpenNextRecentlyUsedEditorInGroupAction extends BaseQuickOpenEditorInGroupAction {
public static ID = 'workbench.action.openNextRecentlyUsedEditorInGroup';
public static LABEL = nls.localize('openNextEditorInGroup', "Open Next Recently Used Editor in Group");
constructor(
id: string,
label: string,
@IQuickOpenService quickOpenService: IQuickOpenService,
@IKeybindingService keybindingService: IKeybindingService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService
) {
super(id, label, quickOpenService, keybindingService, editorGroupService, editorService);
}
}
export class GlobalQuickOpenAction extends Action {
public static ID = 'workbench.action.quickOpen';
@@ -932,11 +932,11 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Validate width ratios
const positions = rightEditors.length ? 3 : centerEditors.length ? 2 : 1;
if (widthRatios.length !== positions) {
if (!widthRatios || widthRatios.length !== positions) {
if (!this.getVisibleEditors().length) {
widthRatios = (positions === 3) ? [0.33, 0.33, 0.34] : (positions === 2) ? [0.5, 0.5] : [1];
} else {
widthRatios = void 0; // being taken care of by the layouting if editors are already open
widthRatios = void 0;
}
}
@@ -11,7 +11,7 @@ import labels = require('vs/base/common/labels');
import URI from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import strings = require('vs/base/common/strings');
import {IAutoFocus, Mode, IEntryRunContext} from 'vs/base/parts/quickopen/common/quickOpen';
import {IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration} from 'vs/base/parts/quickopen/common/quickOpen';
import {QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup} from 'vs/base/parts/quickopen/browser/quickOpenModel';
import scorer = require('vs/base/common/scorer');
import {QuickOpenHandler} from 'vs/workbench/browser/quickopen';
@@ -178,8 +178,8 @@ export abstract class EditorGroupPicker extends BaseEditorPicker {
return nls.localize('noOpenedEditors', "List of opened editors is currently empty");
}
public getAutoFocus(searchValue: string, isQuickNavigating?: boolean): IAutoFocus {
if (searchValue || !isQuickNavigating) {
public getAutoFocus(searchValue: string, quickNavigateConfiguration: IQuickNavigateConfiguration): IAutoFocus {
if (searchValue || !quickNavigateConfiguration) {
return {
autoFocusFirstEntry: true
};
@@ -191,6 +191,13 @@ export abstract class EditorGroupPicker extends BaseEditorPicker {
return super.getAutoFocus(searchValue);
}
const isShiftNavigate = (quickNavigateConfiguration && quickNavigateConfiguration.keybindings.some(k => k.hasShift()));
if (isShiftNavigate) {
return {
autoFocusLastEntry: true
};
}
return {
autoFocusFirstEntry: group.count === 1,
autoFocusSecondEntry: group.count > 1
@@ -35,10 +35,18 @@
border-left: 1px solid rgba(128, 128, 128, 0.2);
border-bottom: 1px solid rgba(128, 128, 128, 0.2);
box-sizing: border-box;
opacity: 0.7 !important;
padding-left: 10px;
}
.vs .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab,
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab {
opacity: 0.7 !important;
}
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab {
opacity: 0.5 !important;
}
.hc-black .monaco-workbench > .part.editor > .content > .one-editor-container > .title.active .tabs-container > .tab.active {
border: 1px solid #f38518;
}
@@ -771,7 +771,7 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe
let placeHolderLabel = (typeof canRun === 'string') ? canRun : nls.localize('canNotRunPlaceholder', "This quick open handler can not be used in the current context");
const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(placeHolderLabel)], this.actionProvider);
this.showModel(model, resolvedHandler.getAutoFocus(value, this.quickOpenWidget.isQuickNavigating()), resolvedHandler.getAriaLabel());
this.showModel(model, resolvedHandler.getAutoFocus(value, this.quickOpenWidget.getQuickNavigateConfiguration()), resolvedHandler.getAriaLabel());
return TPromise.as(null);
}
@@ -792,9 +792,9 @@ export class QuickOpenController extends WorkbenchComponent implements IQuickOpe
if (this.currentResultToken === currentResultToken) {
if (!result || !result.entries.length) {
const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(resolvedHandler.getEmptyLabel(value))]);
this.showModel(model, resolvedHandler.getAutoFocus(value, this.quickOpenWidget.isQuickNavigating()), resolvedHandler.getAriaLabel());
this.showModel(model, resolvedHandler.getAutoFocus(value, this.quickOpenWidget.getQuickNavigateConfiguration()), resolvedHandler.getAriaLabel());
} else {
this.showModel(result, resolvedHandler.getAutoFocus(value, this.quickOpenWidget.isQuickNavigating()), resolvedHandler.getAriaLabel());
this.showModel(result, resolvedHandler.getAutoFocus(value, this.quickOpenWidget.getQuickNavigateConfiguration()), resolvedHandler.getAriaLabel());
}
}
});
+2 -2
View File
@@ -14,7 +14,7 @@ import errors = require('vs/base/common/errors');
import {Registry} from 'vs/platform/platform';
import {Action} from 'vs/base/common/actions';
import {KeyMod} from 'vs/base/common/keyCodes';
import {Mode, IEntryRunContext, IAutoFocus, IModel} from 'vs/base/parts/quickopen/common/quickOpen';
import {Mode, IEntryRunContext, IAutoFocus, IModel, IQuickNavigateConfiguration} from 'vs/base/parts/quickopen/common/quickOpen';
import {QuickOpenEntry, IHighlight, QuickOpenEntryGroup, QuickOpenModel} from 'vs/base/parts/quickopen/browser/quickOpenModel';
import {EditorOptions, EditorInput} from 'vs/workbench/common/editor';
import {IResourceInput, IEditorInput} from 'vs/platform/editor/common/editor';
@@ -63,7 +63,7 @@ export class QuickOpenHandler {
* Indicates if the handler wishes the quick open widget to automatically select the first result entry or an entry
* based on a specific prefix match.
*/
public getAutoFocus(searchValue: string, isQuickNavigating?: boolean): IAutoFocus {
public getAutoFocus(searchValue: string, quickNavigateConfiguration?: IQuickNavigateConfiguration): IAutoFocus {
return {};
}
+10 -3
View File
@@ -151,7 +151,14 @@ export abstract class ViewerViewlet extends Viewlet {
/**
* A viewlet descriptor is a leightweight descriptor of a viewlet in the workbench.
*/
export class ViewletDescriptor extends CompositeDescriptor<Viewlet> { }
export class ViewletDescriptor extends CompositeDescriptor<Viewlet> {
public isGlobal: boolean;
constructor(moduleId: string, ctorName: string, id: string, name: string, cssClass?: string, order?: number, isGlobal?: boolean) {
super(moduleId, ctorName, id, name, cssClass, order);
this.isGlobal = isGlobal || false;
}
}
export const Extensions = {
Viewlets: 'workbench.contributions.viewlets'
@@ -171,14 +178,14 @@ export class ViewletRegistry extends CompositeRegistry<Viewlet> {
* Returns the viewlet descriptor for the given id or null if none.
*/
public getViewlet(id: string): ViewletDescriptor {
return this.getComposite(id);
return this.getComposite(id) as ViewletDescriptor;
}
/**
* Returns an array of registered viewlets known to the platform.
*/
public getViewlets(): ViewletDescriptor[] {
return this.getComposits();
return this.getComposits() as ViewletDescriptor[];
}
/**
+2
View File
@@ -35,6 +35,8 @@ exports.collectModules = function(excludes) {
createModuleDescription('vs/workbench/parts/debug/browser/repl', excludes),
createModuleDescription('vs/workbench/parts/debug/node/telemetryApp', []),
createModuleDescription('vs/workbench/parts/extensions/electron-browser/extensionsViewlet', excludes),
createModuleDescription('vs/workbench/parts/markers/browser/markersPanel', excludes),
createModuleDescription('vs/workbench/parts/terminal/electron-browser/terminalPanel', excludes),
@@ -82,19 +82,22 @@ export function renderVariable(tree: tree.ITree, variable: model.Variable, data:
}
}
function renderRenameBox(debugService: debug.IDebugService, contextViewService: IContextViewService, tree: tree.ITree, element: any,
container: HTMLElement, initialValue: string, placeholder: string, ariaLabel: string): void {
interface IRenameBoxOptions {
initialValue: string;
ariaLabel: string;
placeholder?: string;
validationOptions?: inputbox.IInputValidationOptions;
}
function renderRenameBox(debugService: debug.IDebugService, contextViewService: IContextViewService, tree: tree.ITree, element: any, container: HTMLElement, options: IRenameBoxOptions): void {
let inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
let inputBox = new inputbox.InputBox(inputBoxContainer, contextViewService, {
validationOptions: {
validation: null,
showMessage: false
},
placeholder: placeholder,
ariaLabel: ariaLabel
validationOptions: options.validationOptions,
placeholder: options.placeholder,
ariaLabel: options.ariaLabel
});
inputBox.value = initialValue ? initialValue : '';
inputBox.value = options.initialValue ? options.initialValue : '';
inputBox.focus();
let disposed = false;
@@ -111,10 +114,13 @@ function renderRenameBox(debugService: debug.IDebugService, contextViewService:
debugService.renameFunctionBreakpoint(element.getId(), inputBox.value).done(null, errors.onUnexpectedError);
} else if (element instanceof model.FunctionBreakpoint && !element.name) {
debugService.removeFunctionBreakpoints(element.getId()).done(null, errors.onUnexpectedError);
} else if (element instanceof model.Variable && renamed) {
debugService.setVariable(element, inputBox.value)
// if everything went fine we need to refresh that tree element since his value updated
.done(() => tree.refresh(element, false), errors.onUnexpectedError);
} else if (element instanceof model.Variable) {
(<model.Variable>element).errorMessage = null;
if (renamed) {
debugService.setVariable(element, inputBox.value)
// if everything went fine we need to refresh that tree element since his value updated
.done(() => tree.refresh(element, false), errors.onUnexpectedError);
}
}
tree.clearHighlight();
@@ -262,7 +268,7 @@ export class CallStackController extends BaseDebugController {
return true;
}
private focusStackFrame(stackFrame: debug.IStackFrame, event: IKeyboardEvent|IMouseEvent, preserveFocus: boolean): void {
private focusStackFrame(stackFrame: debug.IStackFrame, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void {
this.debugService.setFocusedStackFrameAndEvaluate(stackFrame).done(null, errors.onUnexpectedError);
const sideBySide = (event && (event.ctrlKey || event.metaKey));
@@ -636,11 +642,17 @@ export class VariablesRenderer implements tree.IRenderer {
if (templateId === VariablesRenderer.SCOPE_TEMPLATE_ID) {
this.renderScope(element, templateData);
} else {
if (element === this.debugService.getViewModel().getSelectedExpression()) {
renderRenameBox(this.debugService, this.contextViewService, tree, element, (<IVariableTemplateData>templateData).expression,
(<model.Variable>element).value, null, nls.localize('variableValueAriaLabel', "Type new variable value"));
const variable = <model.Variable>element;
if (variable === this.debugService.getViewModel().getSelectedExpression() || variable.errorMessage) {
renderRenameBox(this.debugService, this.contextViewService, tree, variable, (<IVariableTemplateData>templateData).expression, {
initialValue: variable.value,
ariaLabel: nls.localize('variableValueAriaLabel', "Type new variable value"),
validationOptions: {
validation: (value: string) => variable.errorMessage ? ({ content: variable.errorMessage }) : null
}
});
} else {
renderVariable(tree, element, templateData, true);
renderVariable(tree, variable, templateData, true);
}
}
}
@@ -833,7 +845,11 @@ export class WatchExpressionsRenderer implements tree.IRenderer {
private renderWatchExpression(tree: tree.ITree, watchExpression: debug.IExpression, data: IWatchExpressionTemplateData): void {
let selectedExpression = this.debugService.getViewModel().getSelectedExpression();
if ((selectedExpression instanceof model.Expression && selectedExpression.getId() === watchExpression.getId()) || (watchExpression instanceof model.Expression && !watchExpression.name)) {
renderRenameBox(this.debugService, this.contextViewService, tree, watchExpression, data.expression, watchExpression.name, nls.localize('watchExpressionPlaceholder', "Expression to watch"), nls.localize('watchExpressionInputAriaLabel', "Type watch expression"));
renderRenameBox(this.debugService, this.contextViewService, tree, watchExpression, data.expression, {
initialValue: watchExpression.name,
placeholder: nls.localize('watchExpressionPlaceholder', "Expression to watch"),
ariaLabel: nls.localize('watchExpressionInputAriaLabel', "Type watch expression")
});
}
data.actionBar.context = watchExpression;
@@ -1106,7 +1122,11 @@ export class BreakpointsRenderer implements tree.IRenderer {
private renderFunctionBreakpoint(tree: tree.ITree, functionBreakpoint: debug.IFunctionBreakpoint, data: IFunctionBreakpointTemplateData): void {
const selected = this.debugService.getViewModel().getSelectedFunctionBreakpoint();
if (!functionBreakpoint.name || (selected && selected.getId() === functionBreakpoint.getId())) {
renderRenameBox(this.debugService, this.contextViewService, tree, functionBreakpoint, data.breakpoint, functionBreakpoint.name, nls.localize('functionBreakpointPlaceholder', "Function to break on"), nls.localize('functionBreakPointInputAriaLabel', "Type function breakpoint"));
renderRenameBox(this.debugService, this.contextViewService, tree, functionBreakpoint, data.breakpoint, {
initialValue: functionBreakpoint.name,
placeholder: nls.localize('functionBreakpointPlaceholder', "Function to break on"),
ariaLabel: nls.localize('functionBreakPointInputAriaLabel', "Type function breakpoint")
});
} else {
this.debugService.getModel().areBreakpointsActivated() ? tree.removeTraits('disabled', [functionBreakpoint]) : tree.addTraits('disabled', [functionBreakpoint]);
data.name.textContent = functionBreakpoint.name;
@@ -1222,7 +1242,7 @@ export class BreakpointsController extends BaseDebugController {
return false;
}
private openBreakpointSource(breakpoint: debug.IBreakpoint, event: IKeyboardEvent|IMouseEvent, preserveFocus: boolean): void {
private openBreakpointSource(breakpoint: debug.IBreakpoint, event: IKeyboardEvent | IMouseEvent, preserveFocus: boolean): void {
if (!breakpoint.source.inMemory) {
const sideBySide = (event && (event.ctrlKey || event.metaKey));
this.debugService.openOrRevealSource(breakpoint.source, breakpoint.lineNumber, preserveFocus, sideBySide).done(null, errors.onUnexpectedError);
@@ -246,6 +246,7 @@
.debug-viewlet .monaco-inputbox > .wrapper > .input {
padding: 0px;
color: initial;
}
.debug-viewlet .debug-action.add-watch-expression,
@@ -226,11 +226,12 @@ export class KeyValueOutputElement extends OutputElement {
}
}
export class ExpressionContainer implements debug.IExpressionContainer {
export abstract class ExpressionContainer implements debug.IExpressionContainer {
private children: TPromise<debug.IExpression[]>;
public valueChanged: boolean;
public static allValues: { [id: string]: string } = {};
private _value: string;
constructor(public reference: number, private id: string, private cacheChildren: boolean) {
this.children = null;
@@ -251,20 +252,6 @@ export class ExpressionContainer implements debug.IExpressionContainer {
return this.id;
}
}
export class Expression extends ExpressionContainer implements debug.IExpression {
static DEFAULT_VALUE = 'not available';
public available: boolean;
private _value: string;
constructor(public name: string, cacheChildren: boolean, id = uuid.generateUuid()) {
super(0, id, cacheChildren);
this.value = Expression.DEFAULT_VALUE;
this.available = false;
}
public get value(): string {
return this._value;
}
@@ -277,15 +264,26 @@ export class Expression extends ExpressionContainer implements debug.IExpression
}
}
export class Expression extends ExpressionContainer implements debug.IExpression {
static DEFAULT_VALUE = 'not available';
public available: boolean;
constructor(public name: string, cacheChildren: boolean, id = uuid.generateUuid()) {
super(0, id, cacheChildren);
this.value = Expression.DEFAULT_VALUE;
this.available = false;
}
}
export class Variable extends ExpressionContainer implements debug.IExpression {
public value: string;
// Used to show the error message coming from the adapter when setting the value #7807
public errorMessage: string;
constructor(public parent: debug.IExpressionContainer, reference: number, public name: string, value: string, public available = true) {
super(reference, `variable:${ parent.getId() }:${ name }`, true);
this.value = massageValue(value);
this.valueChanged = ExpressionContainer.allValues[this.getId()] && ExpressionContainer.allValues[this.getId()] !== value;
ExpressionContainer.allValues[this.getId()] = value;
}
}
@@ -492,7 +492,7 @@ export class DebugService implements debug.IDebugService {
name: variable.name,
value,
variablesReference: (<model.Variable>variable).parent.reference
}).then(response => variable.value = response.body.value);
}).then(response => variable.value = response.body.value, err => (<model.Variable>variable).errorMessage = err.message);
}
public addWatchExpression(name: string): TPromise<void> {
@@ -646,6 +646,11 @@ export class DebugService implements debug.IDebugService {
isBuiltin: this.configurationManager.adapter.extensionDescription.isBuiltin
});
}).then(undefined, (error: any) => {
if (error instanceof Error && error.message === 'Canceled') {
// Do not show 'canceled' error messages to the user #7906
return TPromise.as(null);
}
this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined });
this.setStateAndEmit(debug.State.Inactive);
if (this.session) {
@@ -6,6 +6,7 @@
import stream = require('stream');
import uuid = require('vs/base/common/uuid');
import { TPromise } from 'vs/base/common/winjs.base';
import { canceled } from 'vs/base/common/errors';
export abstract class V8Protocol {
@@ -41,7 +42,9 @@ export abstract class V8Protocol {
}
protected send(command: string, args: any): TPromise<DebugProtocol.Response> {
let errorCallback;
return new TPromise((completeDispatch, errorDispatch) => {
errorCallback = errorDispatch;
this.doSend(command, args, (result: DebugProtocol.Response) => {
if (result.success) {
completeDispatch(result);
@@ -49,7 +52,7 @@ export abstract class V8Protocol {
errorDispatch(result);
}
});
});
}, () => errorCallback(canceled()));
}
private doSend(command: string, args: any, clb: (result: DebugProtocol.Response) => void): void {
@@ -19,7 +19,7 @@ export class EditorAccessor implements emmet.Editor {
lineStarts: number[] = null;
emmetSupportedModes = ['html', 'razor', 'css', 'less', 'scss', 'xml', 'xsl', 'jade', 'handlebars', 'ejs', 'hbs', 'jsx', 'tsx', 'erb', 'php', 'twig'];
emmetSupportedModes = ['html', 'razor', 'css', 'less', 'sass', 'scss', 'stylus', 'xml', 'xsl', 'jade', 'handlebars', 'ejs', 'hbs', 'jsx', 'tsx', 'erb', 'php', 'twig'];
constructor(editor: ICommonCodeEditor) {
this.editor = editor;
@@ -121,8 +121,8 @@ export class EditorAccessor implements emmet.Editor {
if (/\b(typescriptreact|javascriptreact)\b/.test(syntax)) { // treat like tsx like jsx
return 'jsx';
}
if (syntax === 'stylus') { // map stylus to css
return 'css';
if (syntax === 'sass-indented') { // map sass-indented to sass
return 'sass';
}
return syntax;
}
@@ -15,13 +15,11 @@ import nls = require('vs/nls');
export abstract class EmmetEditorAction extends EditorAction {
protected static editorAccessor: EditorAccessor;
protected editorAccessor: EditorAccessor;
constructor(descriptor: IEditorActionDescriptorData, editor: ICommonCodeEditor) {
super(descriptor, editor, Behaviour.TextFocus);
if (!EmmetEditorAction.editorAccessor) {
EmmetEditorAction.editorAccessor = new EditorAccessor(editor);
}
this.editorAccessor = new EditorAccessor(editor);
}
abstract runEmmetAction(_module: any);
@@ -30,15 +28,15 @@ export abstract class EmmetEditorAction extends EditorAction {
return new TPromise((c, e) => {
require(['emmet'], (_module) => {
try {
if (!EmmetEditorAction.editorAccessor.isEmmetEnabledMode()) {
EmmetEditorAction.editorAccessor.noExpansionOccurred();
if (!this.editorAccessor.isEmmetEnabledMode()) {
this.editorAccessor.noExpansionOccurred();
return;
}
this.runEmmetAction(_module);
} catch (err) {
//
} finally {
EmmetEditorAction.editorAccessor.flushCache();
this.editorAccessor.flushCache();
}
}, e);
});
@@ -53,8 +51,8 @@ export class ExpandAbbreviationAction extends EmmetEditorAction {
}
public runEmmetAction(_module) {
if (!_module.run('expand_abbreviation', EmmetEditorAction.editorAccessor)) {
EmmetEditorAction.editorAccessor.noExpansionOccurred();
if (!_module.run('expand_abbreviation', this.editorAccessor)) {
this.editorAccessor.noExpansionOccurred();
}
}
}
@@ -67,8 +65,8 @@ export class RemoveTagAction extends EmmetEditorAction {
}
public runEmmetAction(_module) {
if (!_module.run('remove_tag', EmmetEditorAction.editorAccessor)) {
EmmetEditorAction.editorAccessor.noExpansionOccurred();
if (!_module.run('remove_tag', this.editorAccessor)) {
this.editorAccessor.noExpansionOccurred();
}
}
}
@@ -91,8 +89,8 @@ export class UpdateTagAction extends EmmetEditorAction {
}
private wrapAbbreviation(_module: any, tag) {
if (!_module.run('update_tag', EmmetEditorAction.editorAccessor, tag)) {
EmmetEditorAction.editorAccessor.noExpansionOccurred();
if (!_module.run('update_tag', this.editorAccessor, tag)) {
this.editorAccessor.noExpansionOccurred();
}
}
}
@@ -115,8 +113,8 @@ export class WrapWithAbbreviationAction extends EmmetEditorAction {
}
private wrapAbbreviation(_module: any, abbreviation) {
if (!_module.run('wrap_with_abbreviation', EmmetEditorAction.editorAccessor, abbreviation)) {
EmmetEditorAction.editorAccessor.noExpansionOccurred();
if (!_module.run('wrap_with_abbreviation', this.editorAccessor, abbreviation)) {
this.editorAccessor.noExpansionOccurred();
}
}
}
@@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* 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 { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor';
// TODO@joao: layer breaker
import { IExtension, ExtensionsModel } from '../electron-browser/extensionsModel';
export class ExtensionsInput extends EditorInput {
static get ID() { return 'workbench.extensions.input2'; }
get model(): ExtensionsModel { return this._model; }
get extension(): IExtension { return this._extension; }
constructor(private _model: ExtensionsModel, private _extension: IExtension) {
super();
}
getTypeId(): string {
return ExtensionsInput.ID;
}
getName(): string {
return this.extension.displayName;
}
matches(other: any): boolean {
if (!(other instanceof ExtensionsInput)) {
return false;
}
const otherExtensionInput = other as ExtensionsInput;
// TODO@joao is this correct?
return this.extension === otherExtensionInput.extension;
}
resolve(refresh?: boolean): TPromise<any> {
return TPromise.as(null);
}
}
@@ -0,0 +1,161 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/extensionEditor';
import { TPromise } from 'vs/base/common/winjs.base';
import { marked } from 'vs/base/common/marked/marked';
import { IDisposable, empty, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { Builder } from 'vs/base/browser/builder';
import { append, emmet as $, addClass, removeClass, finalHandler } from 'vs/base/browser/dom';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IRequestService } from 'vs/platform/request/common/request';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionsInput } from '../common/extensionsInput';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITemplateData } from './extensionsList';
import { RatingsWidget, Label } from './extensionsWidgets';
import { EditorOptions } from 'vs/workbench/common/editor';
import { shell } from 'electron';
import product from 'vs/platform/product';
import { IExtensionsViewlet } from './extensions';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { CombinedInstallAction, UpdateAction } from './extensionsActions';
const actionOptions = { icon: true, label: true };
export class ExtensionEditor extends BaseEditor {
static ID: string = 'workbench.editor.extension';
private icon: HTMLElement;
private name: HTMLAnchorElement;
private publisher: HTMLAnchorElement;
private installCount: HTMLElement;
private rating: HTMLAnchorElement;
private description: HTMLElement;
private actionBar: ActionBar;
private body: HTMLElement;
private _highlight: ITemplateData;
private highlightDisposable: IDisposable;
private transientDisposables: IDisposable[];
private disposables: IDisposable[];
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
@IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService private instantiationService: IInstantiationService,
@IRequestService private requestService: IRequestService,
@IViewletService private viewletService: IViewletService
) {
super(ExtensionEditor.ID, telemetryService);
this._highlight = null;
this.highlightDisposable = empty;
this.disposables = [];
}
createEditor(parent: Builder): void {
const container = parent.getHTMLElement();
const root = append(container, $('.extension-editor'));
const header = append(root, $('.header'));
this.icon = append(header, $('.icon'));
const details = append(header, $('.details'));
this.name = append(details, $<HTMLAnchorElement>('a.name'));
this.name.href = '#';
const subtitle = append(details, $('.subtitle'));
this.publisher = append(subtitle, $<HTMLAnchorElement>('a.publisher'));
this.publisher.href = '#';
const install = append(subtitle, $('span.install'));
append(install, $('span.octicon.octicon-cloud-download'));
this.installCount = append(install, $('span.count'));
this.rating = append(subtitle, $<HTMLAnchorElement>('a.rating'));
this.rating.href = '#';
this.description = append(details, $('.description'));
const actions = append(details, $('.actions'));
this.actionBar = new ActionBar(actions, { animated: false });
this.body = append(root, $('.body'));
}
setInput(input: ExtensionsInput, options: EditorOptions): TPromise<void> {
this.transientDisposables = dispose(this.transientDisposables);
this.body.innerHTML = '';
let promise = TPromise.as(null);
const extension = input.extension;
this.icon.style.backgroundImage = `url("${ extension.iconUrl }")`;
this.name.textContent = extension.displayName;
this.publisher.textContent = extension.publisherDisplayName;
this.description.textContent = extension.description;
if (product.extensionsGallery) {
const extensionUrl = `${ product.extensionsGallery.itemUrl }?itemName=${ extension.publisher }.${ extension.name }`;
this.name.onclick = finalHandler(() => shell.openExternal(extensionUrl));
this.rating.onclick = finalHandler(() => shell.openExternal(`${ extensionUrl }#review-details`));
this.publisher.onclick = finalHandler(() => {
this.viewletService.openViewlet('workbench.viewlet.extensions', true)
.then(viewlet => viewlet as IExtensionsViewlet)
.done(viewlet => viewlet.search(`publisher:"${ extension.publisherDisplayName }"`, true));
});
}
const install = new Label(this.installCount, input.model, extension, e => `${ e.installCount }`);
this.transientDisposables.push(install);
const ratings = new RatingsWidget(this.rating, input.model, extension);
this.transientDisposables.push(ratings);
const installAction = new CombinedInstallAction(input.model, extension);
const updateAction = new UpdateAction(input.model, extension);
this.actionBar.clear();
this.actionBar.push([updateAction, installAction], actionOptions);
this.transientDisposables.push(updateAction, installAction);
addClass(this.body, 'loading');
if (extension.readmeUrl) {
promise = super.setInput(input, options)
.then(() => this.requestService.makeRequest({ url: extension.readmeUrl }))
.then(response => response.responseText)
.then(marked.parse)
.then<void>(html => this.body.innerHTML = html)
.then(null, () => null)
.then(() => removeClass(this.body, 'loading'));
}
this.transientDisposables.push(toDisposable(() => promise.cancel()));
return TPromise.as(null);
}
layout(): void {
return;
}
dispose(): void {
this._highlight = null;
this.transientDisposables = dispose(this.transientDisposables);
this.disposables = dispose(this.disposables);
super.dispose();
}
}
@@ -8,7 +8,7 @@ import {forEach} from 'vs/base/common/collections';
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
import {TPromise as Promise} from 'vs/base/common/winjs.base';
import {match} from 'vs/base/common/glob';
import {IExtensionGalleryService, IExtensionTipsService, IExtension} from 'vs/platform/extensionManagement/common/extensionManagement';
import {IExtensionGalleryService, IExtensionTipsService, ILocalExtension} from 'vs/platform/extensionManagement/common/extensionManagement';
import {IModelService} from 'vs/editor/common/services/modelService';
import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
@@ -62,10 +62,10 @@ export class ExtensionTipsService implements IExtensionTipsService {
}
}
getRecommendations(): Promise<IExtension[]> {
const ids = Object.keys(this._recommendations);
getRecommendations(): Promise<ILocalExtension[]> {
const names = Object.keys(this._recommendations);
return this._galleryService.query({ ids, pageSize: ids.length })
return this._galleryService.query({ names, pageSize: names.length })
.then(result => result.firstPage, () => []);
}
@@ -4,16 +4,35 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/extensions';
import { localize } from 'vs/nls';
import { Registry } from 'vs/platform/platform';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IStatusbarRegistry, Extensions as StatusbarExtensions, StatusbarItemDescriptor, StatusbarAlignment } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { ExtensionsStatusbarItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets';
import { IExtensionGalleryService, IExtensionTipsService, ExtensionsLabel, ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { ExtensionsWorkbenchExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension';
import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/parts/output/common/output';
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/parts/editor/baseEditor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput';
// import { EditorInput } from 'vs/workbench/common/editor';
// import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { ExtensionEditor } from './extensionEditor';
// class ExtensionsInputFactory implements IEditorInputFactory {
// constructor() {}
// public serialize(editorInput: EditorInput): string {
// return '';
// }
// public deserialize(instantiationService: IInstantiationService, resourceRaw: string): EditorInput {
// return instantiationService.createInstance(ExtensionsInput);
// }
// }
registerSingleton(IExtensionGalleryService, ExtensionGalleryService);
registerSingleton(IExtensionTipsService, ExtensionTipsService);
@@ -21,8 +40,31 @@ registerSingleton(IExtensionTipsService, ExtensionTipsService);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(ExtensionsWorkbenchExtension);
Registry.as<IStatusbarRegistry>(StatusbarExtensions.Statusbar)
.registerStatusbarItem(new StatusbarItemDescriptor(ExtensionsStatusbarItem, StatusbarAlignment.LEFT,10000));
Registry.as<IOutputChannelRegistry>(OutputExtensions.OutputChannels)
.registerChannel(ExtensionsChannelId, ExtensionsLabel);
.registerChannel(ExtensionsChannelId, ExtensionsLabel);
// Registry.as<IEditorRegistry>(EditorExtensions.Editors)
// .registerEditorInputFactory(ExtensionsInput.ID, ExtensionsInputFactory);
const editorDescriptor = new EditorDescriptor(
ExtensionEditor.ID,
localize('extension', "Extension"),
'vs/workbench/parts/extensions/electron-browser/extensionEditor',
'ExtensionEditor'
);
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(editorDescriptor, [new SyncDescriptor(ExtensionsInput)]);
const viewletDescriptor = new ViewletDescriptor(
'vs/workbench/parts/extensions/electron-browser/extensionsViewlet',
'ExtensionsViewlet',
'workbench.viewlet.extensions',
localize('extensions', "Extensions"),
'extensions',
100,
true
);
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets)
.registerViewlet(viewletDescriptor);
@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* 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 {IViewlet} from 'vs/workbench/common/viewlet';
export interface IExtensionsViewlet extends IViewlet {
search(text: string, immediate?: boolean): void;
}
@@ -3,216 +3,360 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/extensionActions';
import nls = require('vs/nls');
import { Promise, TPromise } from 'vs/base/common/winjs.base';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import { assign } from 'vs/base/common/objects';
import Severity from 'vs/base/common/severity';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IMessageService } from 'vs/platform/message/common/message';
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
import { IExtensionManagementService, IExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { extensionEquals, getTelemetryData } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService';
// import { assign } from 'vs/base/common/objects';
// import Severity from 'vs/base/common/severity';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
// import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
// import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
// import { IMessageService } from 'vs/platform/message/common/message';
// import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
import { IExtension, ExtensionsModel, ExtensionState } from './extensionsModel';
// import { extensionEquals, getTelemetryData } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
// import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService';
const CloseAction = new Action('action.close', nls.localize('close', "Close"));
// const CloseAction = new Action('action.close', nls.localize('close', "Close"));
export class ListExtensionsAction extends Action {
// export class ListExtensionsAction extends Action {
static ID = 'workbench.extensions.action.listExtensions';
static LABEL = nls.localize('showInstalledExtensions', "Show Installed Extensions");
// static ID = 'workbench.extensions.action.listExtensions';
// static LABEL = nls.localize('showInstalledExtensions', "Show Installed Extensions");
constructor(
id: string,
label: string,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IQuickOpenService private quickOpenService: IQuickOpenService
) {
super(id, label, null, true);
}
// constructor(
// id: string,
// label: string,
// @IExtensionManagementService private extensionManagementService: IExtensionManagementService,
// @IQuickOpenService private quickOpenService: IQuickOpenService
// ) {
// super(id, label, null, true);
// }
public run(): Promise {
return this.quickOpenService.show('ext ');
}
// run(): Promise {
// return this.quickOpenService.show('ext ');
// }
protected isEnabled(): boolean {
return true;
}
}
// protected isEnabled(): boolean {
// return true;
// }
// }
export class InstallExtensionAction extends Action {
// export class InstallExtensionAction extends Action {
static ID = 'workbench.extensions.action.installExtension';
static LABEL = nls.localize('installExtension', "Install Extension");
// static ID = 'workbench.extensions.action.installExtension';
// static LABEL = nls.localize('installExtension', "Install Extension");
constructor(
id: string,
label: string,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IQuickOpenService private quickOpenService: IQuickOpenService
) {
super(id, label, null, true);
}
// constructor(
// id: string,
// label: string,
// @IExtensionManagementService private extensionManagementService: IExtensionManagementService,
// @IQuickOpenService private quickOpenService: IQuickOpenService
// ) {
// super(id, label, null, true);
// }
public run(): Promise {
return this.quickOpenService.show('ext install ');
}
// run(): Promise {
// return this.quickOpenService.show('ext install ');
// }
protected isEnabled(): boolean {
return true;
}
}
// protected isEnabled(): boolean {
// return true;
// }
// }
export class ListOutdatedExtensionsAction extends Action {
// export class ListOutdatedExtensionsAction extends Action {
static ID = 'workbench.extensions.action.listOutdatedExtensions';
static LABEL = nls.localize('showOutdatedExtensions', "Show Outdated Extensions");
// static ID = 'workbench.extensions.action.listOutdatedExtensions';
// static LABEL = nls.localize('showOutdatedExtensions', "Show Outdated Extensions");
constructor(
id: string,
label: string,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IQuickOpenService private quickOpenService: IQuickOpenService
) {
super(id, label, null, true);
}
// constructor(
// id: string,
// label: string,
// @IExtensionManagementService private extensionManagementService: IExtensionManagementService,
// @IQuickOpenService private quickOpenService: IQuickOpenService
// ) {
// super(id, label, null, true);
// }
public run(): Promise {
return this.quickOpenService.show('ext update ');
}
// run(): Promise {
// return this.quickOpenService.show('ext update ');
// }
protected isEnabled(): boolean {
return true;
}
}
// protected isEnabled(): boolean {
// return true;
// }
// }
export class ListSuggestedExtensionsAction extends Action {
// export class ListSuggestedExtensionsAction extends Action {
static ID = 'workbench.extensions.action.listSuggestedExtensions';
static LABEL = nls.localize('showExtensionRecommendations', "Show Extension Recommendations");
// static ID = 'workbench.extensions.action.listSuggestedExtensions';
// static LABEL = nls.localize('showExtensionRecommendations', "Show Extension Recommendations");
constructor(
id: string,
label: string,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IQuickOpenService private quickOpenService: IQuickOpenService
) {
super(id, label, null, true);
}
// constructor(
// id: string,
// label: string,
// @IExtensionManagementService private extensionManagementService: IExtensionManagementService,
// @IQuickOpenService private quickOpenService: IQuickOpenService
// ) {
// super(id, label, null, true);
// }
public run(): Promise {
return this.quickOpenService.show('ext recommend ');
}
// run(): Promise {
// return this.quickOpenService.show('ext recommend ');
// }
protected isEnabled(): boolean {
return true;
}
}
// protected isEnabled(): boolean {
// return true;
// }
// }
export class InstallAction extends Action {
constructor(
label: string,
@IQuickOpenService protected quickOpenService: IQuickOpenService,
@IExtensionManagementService protected extensionManagementService: IExtensionManagementService,
@IMessageService protected messageService: IMessageService,
@ITelemetryService protected telemetryService: ITelemetryService,
@IInstantiationService protected instantiationService: IInstantiationService
) {
super('extensions.install', label, 'octicon octicon-cloud-download', true);
private disposables: IDisposable[] = [];
constructor(private model: ExtensionsModel, private extension: IExtension) {
super('extensions.install', nls.localize('installAction', "Install"), 'extension-action install', false);
this.disposables.push(this.model.onChange(() => this.updateEnablement()));
this.updateEnablement();
}
public run(extension: IExtension): TPromise<any> {
this.enabled = false;
return this.extensionManagementService.getInstalled()
.then(installed => installed.some(e => extensionEquals(e, extension)))
.then(isUpdate => {
return this.extensionManagementService
.install(extension)
.then(() => this.onSuccess(extension, isUpdate), err => this.onError(err, extension, isUpdate))
.then(() => this.enabled = true)
.then(() => null);
});
private updateEnablement(): void {
this.enabled = this.model.canInstall(this.extension) && this.extension.state === ExtensionState.Uninstalled;
}
private onSuccess(extension: IExtension, isUpdate: boolean) {
this.reportTelemetry(extension, isUpdate, true);
this.messageService.show(Severity.Info, {
message: nls.localize('success-installed', "'{0}' was successfully installed. Restart to enable it.", extension.displayName),
actions: [
CloseAction,
this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, nls.localize('restartNow', "Restart Now"))
]
});
run(): TPromise<any> {
return this.model.install(this.extension);
// this.enabled = false;
// return this.extensionManagementService.getInstalled()
// .then(installed => installed.some(({ manifest }) => extensionEquals(manifest, extension)))
// .then(isUpdate => {
// return this.extensionManagementService
// .install(extension)
// .then(() => this.onSuccess(extension, isUpdate), err => this.onError(err, extension, isUpdate))
// .then(() => this.enabled = true)
// .then(() => null);
// });
}
private onError(err: Error, extension: IExtension, isUpdate: boolean) {
this.reportTelemetry(extension, isUpdate, false);
this.messageService.show(Severity.Error, err);
}
// private onSuccess(extension: IGalleryExtension, isUpdate: boolean) {
// this.reportTelemetry(extension, isUpdate, true);
// this.messageService.show(Severity.Info, {
// message: nls.localize('success-installed', "'{0}' was successfully installed. Restart to enable it.", extension.displayName || extension.name),
// actions: [
// CloseAction,
// this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, nls.localize('restartNow', "Restart Now"))
// ]
// });
// }
private reportTelemetry(extension: IExtension, isUpdate: boolean, success: boolean) {
const event = isUpdate ? 'extensionGallery:update' : 'extensionGallery:install';
const data = assign(getTelemetryData(extension), { success });
// private onError(err: Error, extension: IGalleryExtension, isUpdate: boolean) {
// this.reportTelemetry(extension, isUpdate, false);
// this.messageService.show(Severity.Error, err);
// }
this.telemetryService.publicLog(event, data);
// private reportTelemetry(extension: IGalleryExtension, isUpdate: boolean, success: boolean) {
// const event = isUpdate ? 'extensionGallery:update' : 'extensionGallery:install';
// const data = assign(getTelemetryData(extension), { success });
// this.telemetryService.publicLog(event, data);
// }
dispose(): void {
super.dispose();
this.disposables = dispose(this.disposables);
}
}
export class UninstallAction extends Action {
constructor(
@IQuickOpenService protected quickOpenService: IQuickOpenService,
@IExtensionManagementService protected extensionManagementService: IExtensionManagementService,
@IMessageService protected messageService: IMessageService,
@ITelemetryService protected telemetryService: ITelemetryService,
@IInstantiationService protected instantiationService: IInstantiationService
) {
super('extensions.uninstall', nls.localize('uninstall', "Uninstall Extension"), 'octicon octicon-x', true);
private disposables: IDisposable[] = [];
constructor(private model: ExtensionsModel, private extension: IExtension) {
super('extensions.uninstall', nls.localize('uninstall', "Uninstall"), 'extension-action uninstall', false);
this.disposables.push(this.model.onChange(() => this.updateEnablement()));
this.updateEnablement();
}
public run(extension: IExtension): TPromise<any> {
if (!window.confirm(nls.localize('deleteSure', "Are you sure you want to uninstall '{0}'?", extension.displayName))) {
private updateEnablement(): void {
this.enabled = this.extension.state === ExtensionState.Installed;
}
run(): TPromise<any> {
// const name = extension.manifest.displayName || extension.manifest.name;
if (!window.confirm(nls.localize('deleteSure', "Are you sure you want to uninstall '{0}'?", this.extension.displayName))) {
return TPromise.as(null);
}
this.enabled = false;
return this.model.uninstall(this.extension);
return this.extensionManagementService.getInstalled().then(localExtensions => {
const [local] = localExtensions.filter(local => extensionEquals(local, extension));
// this.enabled = false;
if (!local) {
return TPromise.wrapError(nls.localize('notFound', "Extension '{0}' not installed.", extension.displayName));
}
// return this.extensionManagementService.getInstalled().then(localExtensions => {
// const [local] = localExtensions.filter(local => extensionEquals(local.manifest, extension.manifest));
return this.extensionManagementService.uninstall(local)
.then(() => this.onSuccess(local), err => this.onError(err, local))
.then(() => this.enabled = true)
.then(() => null);
});
// if (!local) {
// return TPromise.wrapError(nls.localize('notFound', "Extension '{0}' not installed.", name));
// }
// return this.extensionManagementService.uninstall(local)
// .then(() => this.onSuccess(local), err => this.onError(err, local))
// .then(() => this.enabled = true)
// .then(() => null);
// });
}
private onSuccess(extension: IExtension) {
this.reportTelemetry(extension, true);
this.messageService.show(Severity.Info, {
message: nls.localize('success-uninstalled', "'{0}' was successfully uninstalled. Restart to deactivate it.", extension.displayName),
actions: [
CloseAction,
this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, nls.localize('restartNow2', "Restart Now"))
]
});
}
// private onSuccess(extension: ILocalExtension) {
// const name = extension.manifest.displayName || extension.manifest.name;
// this.reportTelemetry(extension, true);
private onError(err: Error, extension: IExtension) {
this.reportTelemetry(extension, false);
this.messageService.show(Severity.Error, err);
}
// this.messageService.show(Severity.Info, {
// message: nls.localize('success-uninstalled', "'{0}' was successfully uninstalled. Restart to deactivate it.", name),
// actions: [
// CloseAction,
// this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, nls.localize('restartNow2', "Restart Now"))
// ]
// });
// }
private reportTelemetry(extension: IExtension, success: boolean) {
const data = assign(getTelemetryData(extension), { success });
// private onError(err: Error, extension: ILocalExtension) {
// this.reportTelemetry(extension, false);
// this.messageService.show(Severity.Error, err);
// }
this.telemetryService.publicLog('extensionGallery:uninstall', data);
// private reportTelemetry(extension: ILocalExtension, success: boolean) {
// const data = assign(getTelemetryData(extension), { success });
// this.telemetryService.publicLog('extensionGallery:uninstall', data);
// }
dispose(): void {
super.dispose();
this.disposables = dispose(this.disposables);
}
}
export class CombinedInstallAction extends Action {
private installAction: InstallAction;
private uninstallAction: UninstallAction;
private disposables: IDisposable[] = [];
constructor(private model: ExtensionsModel, private extension: IExtension) {
super('extensions.combinedInstall', '', '', false);
this.installAction = new InstallAction(model, extension);
this.uninstallAction = new UninstallAction(model, extension);
this.disposables.push(this.installAction, this.uninstallAction);
this.disposables.push(this.installAction.addListener2(Action.ENABLED, () => this.update()));
this.disposables.push(this.uninstallAction.addListener2(Action.ENABLED, () => this.update()));
this.update();
}
private update(): void {
if (this.installAction.enabled) {
this.enabled = true;
this.label = this.installAction.label;
this.class = this.installAction.class;
} else if (this.uninstallAction.enabled) {
this.enabled = true;
this.label = this.uninstallAction.label;
this.class = this.uninstallAction.class;
} else if (this.extension.state === ExtensionState.Installing) {
this.enabled = false;
this.label = this.installAction.label;
this.class = this.installAction.class;
} else {
this.enabled = false;
}
}
run(): TPromise<any> {
if (this.installAction.enabled) {
return this.installAction.run();
} else if (this.uninstallAction.enabled) {
return this.uninstallAction.run();
}
return TPromise.as(null);
}
dispose(): void {
super.dispose();
this.disposables = dispose(this.disposables);
}
}
export class UpdateAction extends Action {
private static EnabledClass = 'extension-action update';
private static DisabledClass = `${ UpdateAction.EnabledClass } disabled`;
private disposables: IDisposable[] = [];
constructor(private model: ExtensionsModel, private extension: IExtension) {
super('extensions.update', nls.localize('updateAction', "Update"), UpdateAction.DisabledClass, false);
this.disposables.push(this.model.onChange(() => this.updateEnablement()));
this.updateEnablement();
}
private updateEnablement(): void {
const canInstall = this.model.canInstall(this.extension);
const isInstalled = this.extension.state === ExtensionState.Installed;
this.enabled = canInstall && isInstalled && this.extension.outdated;
this.class = this.enabled ? UpdateAction.EnabledClass : UpdateAction.DisabledClass;
}
run(): TPromise<any> {
return this.model.install(this.extension);
// this.enabled = false;
// return this.extensionManagementService.getInstalled()
// .then(installed => installed.some(({ manifest }) => extensionEquals(manifest, extension)))
// .then(isUpdate => {
// return this.extensionManagementService
// .install(extension)
// .then(() => this.onSuccess(extension, isUpdate), err => this.onError(err, extension, isUpdate))
// .then(() => this.enabled = true)
// .then(() => null);
// });
}
// private onSuccess(extension: IGalleryExtension, isUpdate: boolean) {
// this.reportTelemetry(extension, isUpdate, true);
// this.messageService.show(Severity.Info, {
// message: nls.localize('success-installed', "'{0}' was successfully installed. Restart to enable it.", extension.displayName || extension.name),
// actions: [
// CloseAction,
// this.instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, nls.localize('restartNow', "Restart Now"))
// ]
// });
// }
// private onError(err: Error, extension: IGalleryExtension, isUpdate: boolean) {
// this.reportTelemetry(extension, isUpdate, false);
// this.messageService.show(Severity.Error, err);
// }
// private reportTelemetry(extension: IGalleryExtension, isUpdate: boolean, success: boolean) {
// const event = isUpdate ? 'extensionGallery:update' : 'extensionGallery:install';
// const data = assign(getTelemetryData(extension), { success });
// this.telemetryService.publicLog(event, data);
// }
dispose(): void {
super.dispose();
this.disposables = dispose(this.disposables);
}
}
@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* 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 { append, emmet as $, addClass, removeClass } from 'vs/base/browser/dom';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDelegate } from 'vs/base/browser/ui/list/list';
import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging';
import { IExtension, ExtensionsModel } from './extensionsModel';
import { CombinedInstallAction, UpdateAction } from './extensionsActions';
import { Label } from './extensionsWidgets';
export interface ITemplateData {
extension: IExtension;
element: HTMLElement;
icon: HTMLElement;
name: HTMLElement;
version: HTMLElement;
author: HTMLElement;
description: HTMLElement;
actionbar: ActionBar;
disposables: IDisposable[];
}
export class Delegate implements IDelegate<IExtension> {
getHeight() { return 62; }
getTemplateId() { return 'extension'; }
}
const actionOptions = { icon: true, label: true };
export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
private _templates: ITemplateData[];
get templates(): ITemplateData[] { return this._templates; }
constructor(
private model: ExtensionsModel,
@IInstantiationService private instantiationService: IInstantiationService
) {
this._templates = [];
}
get templateId() { return 'extension'; }
renderTemplate(root: HTMLElement): ITemplateData {
const element = append(root, $('.extension'));
const icon = append(element, $('.icon'));
const details = append(element, $('.details'));
const header = append(details, $('.header'));
const name = append(header, $('span.name.ellipsis'));
const version = append(header, $('span.version.ellipsis'));
const author = append(header, $('span.author.ellipsis'));
const description = append(details, $('.description.ellipsis'));
const actionbar = new ActionBar(details, { animated: false });
const disposables = [];
const result = { extension: null, element, icon, name, version, author, description, actionbar, disposables };
this._templates.push(result);
return result;
}
renderPlaceholder(index: number, data: ITemplateData): void {
data.disposables = dispose(data.disposables);
addClass(data.element, 'loading');
data.extension = null;
data.icon.style.backgroundImage = '';
data.name.textContent = '';
data.version.textContent = '';
data.author.textContent = '';
data.description.textContent = '';
data.actionbar.clear();
}
renderElement(extension: IExtension, index: number, data: ITemplateData): void {
data.disposables = dispose(data.disposables);
removeClass(data.element, 'loading');
data.extension = extension;
data.icon.style.backgroundImage = `url("${ extension.iconUrl }")`;
data.name.textContent = extension.displayName;
data.author.textContent = extension.publisherDisplayName;
data.description.textContent = extension.description;
const version = new Label(data.version, this.model, extension, e => e.version);
const installAction = new CombinedInstallAction(this.model, extension);
const updateAction = new UpdateAction(this.model, extension);
data.actionbar.clear();
data.actionbar.push([updateAction, installAction], actionOptions);
data.disposables.push(version, installAction, updateAction);
}
disposeTemplate(data: ITemplateData): void {
const index = this._templates.indexOf(data);
if (index > -1) {
this._templates.splice(index, 1);
}
}
}
@@ -0,0 +1,333 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/extensionsViewlet';
import Event, { Emitter } from 'vs/base/common/event';
import { index } from 'vs/base/common/arrays';
import { ThrottledDelayer } from 'vs/base/common/async';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IPager, mapPager } from 'vs/base/common/paging';
import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import * as semver from 'semver';
export enum ExtensionState {
Installing,
Installed,
// Uninstalling,
Uninstalled
}
export interface IExtension {
state: ExtensionState;
name: string;
displayName: string;
publisher: string;
publisherDisplayName: string;
version: string;
latestVersion: string;
description: string;
readmeUrl: string;
iconUrl: string;
installCount: number;
rating: number;
ratingCount: number;
outdated: boolean;
}
interface IExtensionStateProvider {
(extension: Extension): ExtensionState;
}
class Extension implements IExtension {
constructor(
private stateProvider: IExtensionStateProvider,
public local: ILocalExtension,
public gallery: IGalleryExtension = null
) {}
get name(): string {
return this.local ? this.local.manifest.name : this.gallery.name;
}
get displayName(): string {
if (this.local) {
return this.local.manifest.displayName || this.local.manifest.name;
}
return this.gallery.displayName || this.gallery.name;
}
get publisher(): string {
return this.local ? this.local.manifest.publisher : this.gallery.publisher;
}
get publisherDisplayName(): string {
if (this.local) {
if (this.local.metadata && this.local.metadata.publisherDisplayName) {
return this.local.metadata.publisherDisplayName;
}
return this.local.manifest.publisher;
}
return this.gallery.publisherDisplayName || this.gallery.publisher;
}
get version(): string {
return this.local ? this.local.manifest.version : this.gallery.versions[0].version;
}
get latestVersion(): string {
return this.gallery ? this.gallery.versions[0].version : this.local.manifest.version;
}
get description(): string {
return this.local ? this.local.manifest.description : this.gallery.description;
}
get readmeUrl(): string {
if (this.local && this.local.readmeUrl) {
return this.local.readmeUrl;
}
if (this.gallery && this.gallery.versions[0].readmeUrl) {
return this.gallery.versions[0].readmeUrl;
}
return null;
}
get iconUrl(): string {
if (this.local && this.local.manifest.icon) {
return `file://${ this.local.path }/${ this.local.manifest.icon }`;
}
if (this.gallery && this.gallery.versions[0].iconUrl) {
return this.gallery.versions[0].iconUrl;
}
return require.toUrl('./media/defaultIcon.png');
}
get state(): ExtensionState {
return this.stateProvider(this);
}
get installCount(): number {
return this.gallery ? this.gallery.installCount : null;
}
get rating(): number {
return this.gallery ? this.gallery.rating : null;
}
get ratingCount(): number {
return this.gallery ? this.gallery.ratingCount : null;
}
get outdated(): boolean {
return semver.gt(this.latestVersion, this.version);
}
}
export class ExtensionsModel {
private static SyncPeriod = 1000 * 60 * 60 * 12; // 12 hours
private stateProvider: IExtensionStateProvider;
private installing: { id: string; extension: Extension; }[] = [];
private installed: Extension[] = [];
private didTriggerSync: boolean = false;
private syncDelayer: ThrottledDelayer<void>;
private disposables: IDisposable[] = [];
private _onChange: Emitter<void> = new Emitter<void>();
get onChange(): Event<void> { return this._onChange.event; }
constructor(
@IExtensionManagementService private extensionService: IExtensionManagementService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService
) {
this.stateProvider = ext => this.getExtensionState(ext);
this.disposables.push(extensionService.onInstallExtension(({ id, gallery }) => this.onInstallExtension(id, gallery)));
this.disposables.push(extensionService.onDidInstallExtension(({ id, local, error }) => this.onDidInstallExtension(id, local, error)));
this.disposables.push(extensionService.onUninstallExtension((id) => this.onUninstallExtension(id)));
this.syncDelayer = new ThrottledDelayer<void>(ExtensionsModel.SyncPeriod);
}
getLocal(): TPromise<IExtension[]> {
return this.extensionService.getInstalled().then(result => {
const installedById = index(this.installed, e => e.local.id);
this.installed = result.map(local => {
const extension = installedById[local.id] || new Extension(this.stateProvider, local);
extension.local = local;
return extension;
});
const installing = this.installing
.filter(e => !this.installed.some(installed => installed.local.id === e.id))
.map(e => e.extension);
if (!this.didTriggerSync) {
this.didTriggerSync = true;
this.syncWithGallery(true);
}
this._onChange.fire();
return [...this.installed, ...installing];
});
}
queryGallery(options: IQueryOptions = {}): TPromise<IPager<IExtension>> {
return this.galleryService.query(options).then(result => {
const installedByGalleryId = index(this.installed, e => e.local.metadata ? e.local.metadata.id : '');
return mapPager(result, gallery => {
const id = gallery.id;
const installed = installedByGalleryId[id];
if (installed) {
installed.gallery = gallery;
this._onChange.fire();
return installed;
}
return new Extension(this.stateProvider, null, gallery);
});
});
}
private syncWithGallery(immediate = false): void {
const loop = () => this.doSyncWithGallery().then(() => this.syncWithGallery());
const delay = immediate ? 0 : ExtensionsModel.SyncPeriod;
this.syncDelayer.trigger(loop, delay);
}
private doSyncWithGallery(): TPromise<void> {
const ids = this.installed
.filter(e => !!(e.local && e.local.metadata))
.map(e => e.local.metadata.id);
if (ids.length === 0) {
return TPromise.as(null);
}
return this.queryGallery({ ids, pageSize: ids.length }) as TPromise<any>;
}
canInstall(extension: IExtension): boolean {
if (!(extension instanceof Extension)) {
return;
}
return !!(extension as Extension).gallery;
}
install(extension: IExtension): TPromise<void> {
if (!(extension instanceof Extension)) {
return;
}
const ext = extension as Extension;
const gallery = ext.gallery;
if (!gallery) {
return TPromise.wrapError<void>(new Error('Missing gallery'));
}
return this.extensionService.install(gallery);
}
uninstall(extension: IExtension): TPromise<void> {
if (!(extension instanceof Extension)) {
return;
}
const ext = extension as Extension;
const local = ext.local || this.installed.filter(e => e.local.metadata && ext.gallery && e.local.metadata.id === ext.gallery.id)[0].local;
if (!local) {
return TPromise.wrapError<void>(new Error('Missing local'));
}
return this.extensionService.uninstall(local);
}
private onInstallExtension(id: string, gallery: IGalleryExtension): void {
if (!gallery) {
return;
}
let extension = this.installed.filter(e => (e.local.metadata && e.local.metadata.id) === gallery.id)[0];
if (!extension) {
extension = new Extension(this.stateProvider, null, gallery);
}
extension.gallery = gallery;
this.installing.push({ id, extension });
this._onChange.fire();
}
private onDidInstallExtension(id: string, local: ILocalExtension, error: Error): void {
const installing = this.installing.filter(e => e.id === id)[0];
if (!installing) {
return;
}
const extension = installing.extension;
extension.local = local;
this.installing = this.installing.filter(e => e.id !== id);
const galleryId = local.metadata && local.metadata.id;
const installed = this.installed.filter(e => (e.local.metadata && e.local.metadata.id) === galleryId)[0];
if (galleryId && installed) {
installed.local = local;
} else {
this.installed.push(extension);
}
this._onChange.fire();
}
private onUninstallExtension(id: string): void {
const previousLength = this.installed.length;
this.installed = this.installed.filter(e => e.local.id !== id);
if (previousLength === this.installed.length) {
return;
}
this._onChange.fire();
}
private getExtensionState(extension: Extension): ExtensionState {
if (this.installed.some(e => e === extension || (e.gallery && extension.gallery && e.gallery.id === extension.gallery.id))) {
return ExtensionState.Installed;
}
if (extension.gallery && this.installing.some(e => e.extension.gallery.id === extension.gallery.id)) {
return ExtensionState.Installing;
}
return ExtensionState.Uninstalled;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
@@ -1,617 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import nls = require('vs/nls');
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { isNumber } from 'vs/base/common/types';
import { PagedModel, mapPager } from 'vs/base/common/paging';
import { ThrottledDelayer } from 'vs/base/common/async';
import * as dom from 'vs/base/browser/dom';
import Severity from 'vs/base/common/severity';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IAutoFocus, Mode, IModel, IDataSource, IRenderer, IRunner, IEntryRunContext, IAccessiblityProvider } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenPagedModel, IPagedRenderer } from 'vs/base/parts/quickopen/common/quickOpenPaging';
import { matchesContiguousSubString } from 'vs/base/common/filters';
import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
import { IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, IExtension, IQueryResult } from 'vs/platform/extensionManagement/common/extensionManagement';
import { InstallAction, UninstallAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions';
import { IMessageService } from 'vs/platform/message/common/message';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common/contextService';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { Action } from 'vs/base/common/actions';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { shell } from 'electron';
import { extensionEquals, getOutdatedExtensions } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
const $ = dom.emmet;
const InstallLabel = nls.localize('install', "Install Extension");
const UpdateLabel = nls.localize('update', "Update Extension");
export interface IHighlights {
id: IHighlight[];
name: IHighlight[];
displayName: IHighlight[];
description: IHighlight[];
}
export enum ExtensionState {
Uninstalled,
Installed,
Outdated
}
export interface IExtensionEntry {
extension: IExtension;
highlights: IHighlights;
state: ExtensionState;
}
interface ITemplateData {
root: HTMLElement;
displayName: HighlightedLabel;
version: HTMLElement;
installCount: HTMLElement;
installCountLabel: HTMLElement;
author: HTMLElement;
actionbar: ActionBar;
description: HighlightedLabel;
disposables: IDisposable[];
}
function getHighlights(input: string, extension: IExtension, nullIfEmpty = true): IHighlights {
const id = matchesContiguousSubString(input, `${ extension.publisher }.${ extension.name }`) || [];
const name = matchesContiguousSubString(input, extension.name) || [];
const displayName = matchesContiguousSubString(input, extension.displayName) || [];
const description = matchesContiguousSubString(input, extension.description) || [];
if (nullIfEmpty && !id.length && !name.length && !displayName.length && !description.length) {
return null;
}
return { id, name, displayName, description };
}
function extensionEntryCompare(one: IExtensionEntry, other: IExtensionEntry): number {
const oneInstallCount = one.extension.galleryInformation ? one.extension.galleryInformation.installCount : 0;
const otherInstallCount = other.extension.galleryInformation ? other.extension.galleryInformation.installCount : 0;
const diff = otherInstallCount - oneInstallCount;
if (diff !== 0) {
return diff;
}
return one.extension.displayName.localeCompare(other.extension.displayName);
}
class OpenLicenseAction extends Action {
constructor(
@IWorkspaceContextService private contextService: IWorkspaceContextService
) {
super('extensions.open-license', nls.localize('license', "License"), '', true);
}
public run(extension: IExtension): TPromise<any> {
const url = `${ this.contextService.getConfiguration().env.extensionsGallery.itemUrl }/${ extension.publisher }.${ extension.name }/license`;
shell.openExternal(url);
return TPromise.as(null);
}
}
class OpenInGalleryAction extends Action {
constructor(
private promptToInstall: boolean,
@IMessageService protected messageService: IMessageService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IInstantiationService protected instantiationService: IInstantiationService
) {
super('extensions.open-in-gallery', nls.localize('readme', "Readme"), '', true);
}
public run(extension: IExtension): TPromise<any> {
const url = `${this.contextService.getConfiguration().env.extensionsGallery.itemUrl}/${ extension.publisher }.${ extension.name }`;
shell.openExternal(url);
if (!this.promptToInstall) {
return TPromise.as(null);
}
const hideMessage = this.messageService.show(Severity.Info, {
message: nls.localize('installPrompt', "Would you like to install '{0}'?", extension.displayName),
actions: [
new Action('cancelaction', nls.localize('cancel', 'Cancel')),
new Action('installNow', nls.localize('installNow', 'Install Now'), null, true, () => {
hideMessage();
const hideInstallMessage = this.messageService.show(Severity.Info, nls.localize('nowInstalling', "'{0}' is being installed...", extension.displayName));
const action = this.instantiationService.createInstance(InstallAction, '');
return action.run(extension).then(r => {
hideInstallMessage();
return TPromise.as(r);
}, e => {
hideInstallMessage();
return TPromise.wrapError(e);
});
})
]
});
return TPromise.as(null);
}
}
class InstallRunner implements IRunner<IExtensionEntry> {
private action: InstallAction;
constructor(
@IInstantiationService private instantiationService: IInstantiationService
) {}
run(entry: IExtensionEntry, mode: Mode, context: IEntryRunContext): boolean {
if (mode === Mode.PREVIEW) {
return false;
}
if (entry.state === ExtensionState.Installed) {
return false;
}
if (!this.action) {
this.action = this.instantiationService.createInstance(InstallAction, InstallLabel);
}
this.action.run(entry.extension).done(null, onUnexpectedError);
return true;
}
}
class AccessibilityProvider implements IAccessiblityProvider<IExtensionEntry> {
public getAriaLabel(entry: IExtensionEntry): string {
return nls.localize('extensionAriaLabel', "{0}, {1}, extensions picker", entry.extension.displayName, entry.extension.description);
}
}
class Renderer implements IPagedRenderer<IExtensionEntry> {
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService
) {}
getHeight(entry: IExtensionEntry): number {
return 48;
}
getTemplateId(entry: IExtensionEntry): string {
return 'extension';
}
renderTemplate(templateId: string, container: HTMLElement): ITemplateData {
// Important to preserve order here.
const root = dom.append(container, $('.extension'));
const firstRow = dom.append(root, $('.row'));
const secondRow = dom.append(root, $('.row'));
const published = dom.append(firstRow, $('.published'));
const displayName = new HighlightedLabel(dom.append(firstRow, $('span.name')));
const installCount = dom.append(firstRow, $('span.install'));
dom.append(installCount, $('span.octicon.octicon-cloud-download'));
const installCountLabel = dom.append(installCount, $('span.installCount'));
const version = dom.append(published, $('span.version'));
const author = dom.append(published, $('span.author'));
return {
root,
author,
displayName,
version,
installCount,
installCountLabel,
actionbar: new ActionBar(dom.append(secondRow, $('.actions'))),
description: new HighlightedLabel(dom.append(secondRow, $('span.description'))),
disposables: []
};
}
renderPlaceholder(index: number, templateId: string, data: ITemplateData): void {
dom.addClass(data.root, 'loading');
data.author.textContent = nls.localize('author', 'Author');
data.displayName.set(nls.localize('name', 'Name'));
data.version.textContent = '0.0.1';
data.installCount.style.display = 'none';
data.installCountLabel.textContent = '';
data.actionbar.clear();
data.description.set(nls.localize('description', 'Description'));
data.disposables = dispose(data.disposables);
}
renderElement(entry: IExtensionEntry, templateId: string, data: ITemplateData): void {
dom.removeClass(data.root, 'loading');
const extension = entry.extension;
const publisher = extension.galleryInformation ? extension.galleryInformation.publisherDisplayName : extension.publisher;
const installCount = extension.galleryInformation ? extension.galleryInformation.installCount : null;
const actionOptions = { icon: true, label: false };
const updateActions = () => {
data.actionbar.clear();
if (entry.extension.galleryInformation) {
data.actionbar.push(this.instantiationService.createInstance(OpenInGalleryAction, entry.state === ExtensionState.Uninstalled), { label: true, icon: false });
data.actionbar.push(this.instantiationService.createInstance(OpenLicenseAction), { label: true, icon: false });
}
switch (entry.state) {
case ExtensionState.Uninstalled:
if (entry.extension.galleryInformation) {
data.actionbar.push(this.instantiationService.createInstance(InstallAction, InstallLabel), actionOptions);
}
break;
case ExtensionState.Installed:
data.actionbar.push(this.instantiationService.createInstance(UninstallAction), actionOptions);
break;
case ExtensionState.Outdated:
data.actionbar.push(this.instantiationService.createInstance(UninstallAction), actionOptions);
data.actionbar.push(this.instantiationService.createInstance(InstallAction, UpdateLabel), actionOptions);
break;
}
};
const onExtensionStateChange = (e: IExtension, state: ExtensionState) => {
if (extensionEquals(e, extension)) {
entry.state = state;
updateActions();
}
};
data.actionbar.context = extension;
updateActions();
data.disposables = dispose(data.disposables);
data.disposables.push(this.extensionManagementService.onDidInstallExtension(e => onExtensionStateChange(e.extension, ExtensionState.Installed)));
data.disposables.push(this.extensionManagementService.onDidUninstallExtension(e => onExtensionStateChange(e, ExtensionState.Uninstalled)));
data.displayName.set(extension.displayName, entry.highlights.displayName);
data.displayName.element.title = extension.name;
data.version.textContent = extension.version;
if (isNumber(installCount)) {
data.installCount.style.display = 'inline';
data.installCountLabel.textContent = String(installCount);
if (!installCount) {
data.installCount.title = nls.localize('installCountZero', "{0} wasn't downloaded yet.", extension.displayName);
} else if (installCount === 1) {
data.installCount.title = nls.localize('installCountOne', "{0} was downloaded once.", extension.displayName);
} else {
data.installCount.title = nls.localize('installCountMultiple', "{0} was downloaded {1} times.", extension.displayName, installCount);
}
} else {
data.installCount.style.display = 'none';
data.installCountLabel.textContent = '';
}
data.author.textContent = publisher;
data.description.set(extension.description, entry.highlights.description);
data.description.element.title = extension.description;
}
disposeTemplate(templateId: string, data: ITemplateData): void {
data.displayName.dispose();
data.description.dispose();
data.disposables = dispose(data.disposables);
}
}
class DataSource implements IDataSource<IExtensionEntry> {
getId(entry: IExtensionEntry): string {
const extension = entry.extension;
if (!extension) {
throw new Error(`Not an extension entry. Found ${ Object.keys(entry).slice(5) },... instead.`);
}
if (extension.galleryInformation) {
return `${ extension.galleryInformation.id }-${ extension.version }`;
}
return `local@${ extension.publisher }.${ extension.name }-${ extension.version }@${ extension.path || '' }`;
}
getLabel(entry: IExtensionEntry): string {
return entry.extension.name;
}
}
class LocalExtensionsModel implements IModel<IExtensionEntry> {
public dataSource = new DataSource();
public renderer: IRenderer<IExtensionEntry>;
public accessibilityProvider: IAccessiblityProvider<IExtensionEntry> = new AccessibilityProvider();
public runner = { run: () => false };
public entries: IExtensionEntry[];
constructor(
private installedExtensions: IExtension[],
private outdatedExtensions: IExtension[],
@IInstantiationService instantiationService: IInstantiationService
) {
this.renderer = instantiationService.createInstance(Renderer);
this.entries = [];
}
public set input(input: string) {
this.entries = this.installedExtensions
.map(extension => ({ extension, highlights: getHighlights(input.trim(), extension) }))
.filter(({ highlights }) => !!highlights)
.map(({ extension, highlights }: { extension: IExtension, highlights: IHighlights }) => {
const [outdatedExt] = this.outdatedExtensions.filter(outdatedExt => extensionEquals(outdatedExt, extension));
return {
extension,
highlights,
state: outdatedExt
? ExtensionState.Outdated
: ExtensionState.Installed
};
})
.sort(extensionEntryCompare);
}
}
export class LocalExtensionsHandler extends QuickOpenHandler {
private modelPromise: TPromise<LocalExtensionsModel>;
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService
) {
super();
this.modelPromise = null;
}
public getAriaLabel(): string {
return nls.localize('localExtensionsHandlerAriaLabel', "Type to narrow down the list of installed extensions");
}
getResults(input: string): TPromise<IModel<IExtensionEntry>> {
if (!this.modelPromise) {
this.modelPromise = TPromise.join<any>([this.extensionManagementService.getInstalled(),
getOutdatedExtensions(this.extensionManagementService, this.galleryService)])
.then(result => this.instantiationService.createInstance(LocalExtensionsModel, result[0], result[1]));
}
return this.modelPromise.then(model => {
model.input = input;
return model;
});
}
getEmptyLabel(input: string): string {
return nls.localize('noExtensionsInstalled', "No extensions found");
}
getAutoFocus(searchValue: string): IAutoFocus {
return { autoFocusFirstEntry: true };
}
onClose(canceled: boolean): void {
this.modelPromise = null;
}
}
export class GalleryExtensionsHandler extends QuickOpenHandler {
private delayer: ThrottledDelayer<any>;
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
@ITelemetryService private telemetryService: ITelemetryService
) {
super();
this.delayer = new ThrottledDelayer(500);
}
public getAriaLabel(): string {
return nls.localize('galleryExtensionsHandlerAriaLabel', "Type to narrow down the list of extensions from the gallery");
}
getResults(text: string): TPromise<IModel<number>> {
return this.extensionManagementService.getInstalled().then(localExtensions => {
return this.delayer.trigger(() => this.galleryService.query({ text })).then((result: IQueryResult) => {
const pager = mapPager(result, extension => {
const [local] = localExtensions.filter(local => extensionEquals(local, extension));
return {
extension,
highlights: getHighlights(text.trim(), extension, false),
state: local
? (local.version === extension.version ? ExtensionState.Installed : ExtensionState.Outdated)
: ExtensionState.Uninstalled
};
});
return new QuickOpenPagedModel<IExtensionEntry>(
new PagedModel(pager),
new DataSource(),
this.instantiationService.createInstance(Renderer),
this.instantiationService.createInstance(InstallRunner)
);
});
});
}
getEmptyLabel(input: string): string {
return nls.localize('noExtensionsToInstall', "No extensions found");
}
getAutoFocus(searchValue: string): IAutoFocus {
return { autoFocusFirstEntry: true };
}
}
class OutdatedExtensionsModel implements IModel<IExtensionEntry> {
public dataSource = new DataSource();
public accessibilityProvider: IAccessiblityProvider<IExtensionEntry> = new AccessibilityProvider();
public renderer: IRenderer<IExtensionEntry>;
public runner: IRunner<IExtensionEntry>;
public entries: IExtensionEntry[];
constructor(
private outdatedExtensions: IExtension[],
@IInstantiationService instantiationService: IInstantiationService
) {
this.renderer = instantiationService.createInstance(Renderer);
this.runner = instantiationService.createInstance(InstallRunner);
this.entries = [];
}
public set input(input: string) {
this.entries = this.outdatedExtensions
.map(extension => ({ extension, highlights: getHighlights(input.trim(), extension) }))
.filter(({ highlights }) => !!highlights)
.map(({ extension, highlights }) => ({
extension,
highlights,
state: ExtensionState.Outdated
}))
.sort(extensionEntryCompare);
}
}
export class OutdatedExtensionsHandler extends QuickOpenHandler {
private modelPromise: TPromise<OutdatedExtensionsModel>;
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
@ITelemetryService private telemetryService: ITelemetryService
) {
super();
}
public getAriaLabel(): string {
return nls.localize('outdatedExtensionsHandlerAriaLabel', "Type to narrow down the list of outdated extensions");
}
getResults(input: string): TPromise<IModel<IExtensionEntry>> {
if (!this.modelPromise) {
this.telemetryService.publicLog('extensionGallery:open');
this.modelPromise = getOutdatedExtensions(this.extensionManagementService, this.galleryService)
.then(outdated => this.instantiationService.createInstance(OutdatedExtensionsModel, outdated));
}
return this.modelPromise.then(model => {
model.input = input;
return model;
});
}
onClose(canceled: boolean): void {
this.modelPromise = null;
}
getEmptyLabel(input: string): string {
return nls.localize('noOutdatedExtensions', "No outdated extensions found");
}
getAutoFocus(searchValue: string): IAutoFocus {
return { autoFocusFirstEntry: true };
}
}
class SuggestedExtensionsModel implements IModel<IExtensionEntry> {
public dataSource = new DataSource();
public renderer: IRenderer<IExtensionEntry>;
public runner: IRunner<IExtensionEntry>;
public entries: IExtensionEntry[];
constructor(
private suggestedExtensions: IExtension[],
private localExtensions: IExtension[],
@IInstantiationService instantiationService: IInstantiationService
) {
this.renderer = instantiationService.createInstance(Renderer);
this.runner = instantiationService.createInstance(InstallRunner);
this.entries = [];
}
public set input(input: string) {
this.entries = this.suggestedExtensions
.map(extension => ({ extension, highlights: getHighlights(input.trim(), extension) }))
.filter(({ extension, highlights }) => {
const local = this.localExtensions.filter(local => extensionEquals(local, extension))[0];
return !local && !!highlights;
})
.map(({ extension, highlights }: { extension: IExtension, highlights: IHighlights }) => {
return {
extension,
highlights,
state: ExtensionState.Uninstalled
};
})
.sort(extensionEntryCompare);
}
}
export class SuggestedExtensionHandler extends QuickOpenHandler {
private modelPromise: TPromise<SuggestedExtensionsModel>;
constructor(
@IExtensionTipsService private extensionTipsService: IExtensionTipsService,
@IInstantiationService private instantiationService: IInstantiationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService
) {
super();
}
getResults(input: string): TPromise<IModel<IExtensionEntry>> {
if (!this.modelPromise) {
this.telemetryService.publicLog('extensionRecommendations:open');
this.modelPromise = TPromise.join<any>([this.extensionTipsService.getRecommendations(), this.extensionManagementService.getInstalled()])
.then(result => this.instantiationService.createInstance(SuggestedExtensionsModel, result[0], result[1]));
}
return this.modelPromise.then(model => {
model.input = input;
return model;
});
}
onClose(canceled: boolean): void {
this.modelPromise = null;
}
getEmptyLabel(input: string): string {
return nls.localize('noRecommendedExtensions', "No recommended extensions");
}
getAutoFocus(searchValue: string): IAutoFocus {
return { autoFocusFirstEntry: true };
}
}
@@ -0,0 +1,177 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/extensionsViewlet';
import { localize } from 'vs/nls';
import { ThrottledDelayer, always } from 'vs/base/common/async';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Builder, Dimension } from 'vs/base/browser/builder';
import { mapEvent, filterEvent } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Viewlet } from 'vs/workbench/browser/viewlet';
import { append, emmet as $ } from 'vs/base/browser/dom';
import { PagedModel, SinglePagePagedModel } from 'vs/base/common/paging';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { PagedList } from 'vs/base/browser/ui/list/listPaging';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Delegate, Renderer } from './extensionsList';
import { ExtensionsModel, IExtension } from './extensionsModel';
import { IExtensionsViewlet } from './extensions';
import { IExtensionManagementService, IExtensionGalleryService, SortBy } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionsInput } from '../common/extensionsInput';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet {
static ID: string = 'workbench.viewlet.extensions';
private searchDelayer: ThrottledDelayer<any>;
private root: HTMLElement;
private searchBox: HTMLInputElement;
private extensionsBox: HTMLElement;
private model: ExtensionsModel;
private list: PagedList<IExtension>;
private disposables: IDisposable[] = [];
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
@IExtensionManagementService private extensionService: IExtensionManagementService,
@IProgressService private progressService: IProgressService,
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
) {
super(ExtensionsViewlet.ID, telemetryService);
this.searchDelayer = new ThrottledDelayer(500);
this.model = instantiationService.createInstance(ExtensionsModel);
}
create(parent: Builder): TPromise<void> {
super.create(parent);
parent.addClass('extensions-viewlet');
this.root = parent.getHTMLElement();
const header = append(this.root, $('.header'));
this.searchBox = append(header, $<HTMLInputElement>('input.search-box'));
this.searchBox.type = 'search';
this.searchBox.placeholder = localize('searchExtensions', "Search Extensions in Marketplace");
this.extensionsBox = append(this.root, $('.extensions'));
const delegate = new Delegate();
const renderer = this.instantiationService.createInstance(Renderer, this.model);
this.list = new PagedList(this.extensionsBox, delegate, [renderer]);
const onRawKeyDown = domEvent(this.searchBox, 'keydown');
const onKeyDown = mapEvent(onRawKeyDown, e => new StandardKeyboardEvent(e));
const onEnter = filterEvent(onKeyDown, e => e.keyCode === KeyCode.Enter);
const onEscape = filterEvent(onKeyDown, e => e.keyCode === KeyCode.Escape);
const onUpArrow = filterEvent(onKeyDown, e => e.keyCode === KeyCode.UpArrow);
const onDownArrow = filterEvent(onKeyDown, e => e.keyCode === KeyCode.DownArrow);
onEnter(() => this.onEnter(), null, this.disposables);
onEscape(() => this.onEscape(), null, this.disposables);
onUpArrow(() => this.onUpArrow(), null, this.disposables);
onDownArrow(() => this.onDownArrow(), null, this.disposables);
const onInput = domEvent(this.searchBox, 'input');
onInput(() => this.triggerSearch(), null, this.disposables);
this.list.onDOMFocus(() => this.searchBox.focus(), null, this.disposables);
this.list.onSelectionChange(e => {
const [extension] = e.elements;
if (!extension) {
return;
}
return this.editorService.openEditor(new ExtensionsInput(this.model, extension));
}, null, this.disposables);
return TPromise.as(null);
}
setVisible(visible:boolean): TPromise<void> {
return super.setVisible(visible).then(() => {
if (visible) {
this.searchBox.focus();
this.searchBox.setSelectionRange(0,this.searchBox.value.length);
this.triggerSearch(true);
} else {
this.list.model = new SinglePagePagedModel([]);
}
});
}
focus(): void {
this.searchBox.focus();
}
layout({ height }: Dimension):void {
this.list.layout(height - 38);
}
search(text: string, immediate = false): void {
this.searchBox.value = text;
this.triggerSearch(immediate);
}
private triggerSearch(immediate = false): void {
const text = this.searchBox.value;
this.searchDelayer.trigger(() => this.doSearch(text), immediate || !text ? 0 : 500);
}
private doSearch(text: string = ''): TPromise<any> {
const progressRunner = this.progressService.show(true);
let promise: TPromise<PagedModel<IExtension>>;
if (!text) {
promise = this.model.getLocal()
.then(result => new SinglePagePagedModel(result));
} else if (/@outdated/i.test(text)) {
promise = this.model.getLocal()
.then(result => result.filter(e => e.outdated))
.then(result => new SinglePagePagedModel(result));
} else if (/@popular/i.test(text)) {
promise = this.model.queryGallery({ sortBy: SortBy.InstallCount })
.then(result => new PagedModel(result));
} else {
promise = this.model.queryGallery({ text })
.then(result => new PagedModel(result));
}
return always(promise, () => progressRunner.done())
.then(model => this.list.model = model);
}
private onEnter(): void {
this.list.setSelection(...this.list.getFocus());
}
private onEscape(): void {
this.searchBox.value = '';
this.triggerSearch(true);
}
private onUpArrow(): void {
this.list.focusPrevious();
}
private onDownArrow(): void {
this.list.focusNext();
}
dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}
@@ -3,168 +3,82 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import Severity from 'vs/base/common/severity';
import { ThrottledDelayer } from 'vs/base/common/async';
import { TPromise } from 'vs/base/common/winjs.base';
import { emmet as $, append, toggleClass } from 'vs/base/browser/dom';
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedPromiseError } from 'vs/base/common/errors';
import { assign } from 'vs/base/common/objects';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { IOutputService } from 'vs/workbench/parts/output/common/output';
import { IExtensionService, IMessage } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionManagementService, IExtensionGalleryService, ExtensionsLabel, ExtensionsChannelId, IExtension, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService';
import { getOutdatedExtensions } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
'use strict';
interface IState {
errors: IMessage[];
installing: IExtensionManifest[];
outdated: IExtension[];
import 'vs/css!./media/extensionsWidgets';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IExtension, ExtensionsModel } from './extensionsModel';
import { append, emmet as $, addClass } from 'vs/base/browser/dom';
export interface IOptions {
small?: boolean;
}
const InitialState: IState = {
errors: [],
installing: [],
outdated: []
};
export class Label implements IDisposable {
function extensionEquals(one: IExtensionManifest, other: IExtensionManifest): boolean {
return one.publisher === other.publisher && one.name === other.name;
}
const OutdatedPeriod = 12 * 60 * 60 * 1000; // every 12 hours
export class ExtensionsStatusbarItem implements IStatusbarItem {
private domNode: HTMLElement;
private state: IState = InitialState;
private outdatedDelayer = new ThrottledDelayer<void>(OutdatedPeriod);
private listener: IDisposable;
constructor(
@IExtensionService private extensionService: IExtensionService,
@IOutputService private outputService: IOutputService,
@IExtensionManagementService protected extensionManagementService: IExtensionManagementService,
@IExtensionGalleryService protected extensionGalleryService: IExtensionGalleryService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IQuickOpenService protected quickOpenService: IQuickOpenService,
@ITelemetryService protected telemetrService: ITelemetryService
) {}
render(container: HTMLElement): IDisposable {
this.domNode = append(container, $('a.extensions-statusbar'));
append(this.domNode, $('.icon'));
this.domNode.onclick = () => this.onClick();
this.checkErrors();
this.checkOutdated();
const disposables = [];
this.extensionManagementService.onInstallExtension(this.onInstallExtension, this, disposables);
this.extensionManagementService.onDidInstallExtension(this.onDidInstallExtension, this, disposables);
this.extensionManagementService.onDidUninstallExtension(this.onDidUninstallExtension, this, disposables);
return combinedDisposable(disposables);
element: HTMLElement,
model: ExtensionsModel,
extension: IExtension,
fn: (extension: IExtension) => string
) {
const render = () => element.textContent = fn(extension);
render();
this.listener = model.onChange(render);
}
private updateState(obj: any): void {
this.state = assign(this.state, obj);
this.onStateChange();
dispose(): void {
this.listener = dispose(this.listener);
}
}
private get hasErrors() { return this.state.errors.length > 0; }
private get isInstalling() { return this.state.installing.length > 0; }
private get hasUpdates() { return this.state.outdated.length > 0; }
export class RatingsWidget implements IDisposable {
private onStateChange(): void {
toggleClass(this.domNode, 'has-errors', this.hasErrors);
toggleClass(this.domNode, 'is-installing', !this.hasErrors && this.isInstalling);
toggleClass(this.domNode, 'has-updates', !this.hasErrors && !this.isInstalling && this.hasUpdates);
static ID: string = 'workbench.editor.extension';
if (this.hasErrors) {
const singular = nls.localize('oneIssue', "Extensions (1 issue)");
const plural = nls.localize('multipleIssues', "Extensions ({0} issues)", this.state.errors.length);
this.domNode.title = this.state.errors.length > 1 ? plural : singular;
} else if (this.isInstalling) {
this.domNode.title = nls.localize('extensionsInstalling', "Extensions ({0} installing...)", this.state.installing.length);
} else if (this.hasUpdates) {
const singular = nls.localize('oneUpdate', "Extensions (1 update available)");
const plural = nls.localize('multipleUpdates', "Extensions ({0} updates available)", this.state.outdated.length);
this.domNode.title = this.state.outdated.length > 1 ? plural : singular;
} else {
this.domNode.title = nls.localize('extensions', "Extensions");
private disposables: IDisposable[] = [];
constructor(
private container: HTMLElement,
private model: ExtensionsModel,
private extension: IExtension,
options: IOptions = {}
) {
this.disposables.push(this.model.onChange(() => this.render()));
addClass(container, 'extension-ratings');
if (options.small) {
addClass(container, 'small');
}
this.render();
}
private onClick(): void {
if (this.hasErrors) {
this.telemetrService.publicLog('extensionWidgetClick', {mode : 'hasErrors'});
this.showErrors(this.state.errors);
this.updateState({ errors: [] });
} else if (this.hasUpdates) {
this.telemetrService.publicLog('extensionWidgetClick', {mode : 'hasUpdate'});
this.quickOpenService.show(`ext update `);
} else {
this.telemetrService.publicLog('extensionWidgetClick', {mode : 'none'});
this.quickOpenService.show(`>${ExtensionsLabel}: `);
private render(): void {
const rating = this.extension.rating;
this.container.innerHTML = '';
if (rating === null) {
return;
}
for (let i = 1; i <= 5; i++) {
if (rating >= i) {
append(this.container, $('span.full.star'));
} else if (rating >= i - 0.5) {
append(this.container, $('span.half.star'));
} else {
append(this.container, $('span.empty.star'));
}
}
const count = append(this.container, $('span.count'));
count.textContent = String(this.extension.ratingCount);
}
private showErrors(errors: IMessage[]): void {
const promise = onUnexpectedPromiseError(this.extensionManagementService.getInstalled());
promise.done(installed => {
errors.forEach(m => {
const extension = installed.filter(ext => ext.path === m.source).pop();
const name = extension && extension.name;
const message = name ? `${ name }: ${ m.message }` : m.message;
const outputChannel = this.outputService.getChannel(ExtensionsChannelId);
outputChannel.append(message);
outputChannel.show(true);
});
});
dispose(): void {
this.disposables = dispose(this.disposables);
}
private onInstallExtension(manifest: IExtensionManifest): void {
const installing = [...this.state.installing, manifest];
this.updateState({ installing });
}
private onDidInstallExtension({ extension }: { extension: IExtension; }): void {
const installing = this.state.installing
.filter(e => !extensionEquals(extension, e));
this.updateState({ installing });
this.outdatedDelayer.trigger(() => this.checkOutdated(), 0);
}
private onDidUninstallExtension(): void {
this.outdatedDelayer.trigger(() => this.checkOutdated(), 0);
}
private checkErrors(): void {
const promise = onUnexpectedPromiseError(this.extensionService.onReady());
promise.done(() => {
const status = this.extensionService.getExtensionsStatus();
const errors = Object.keys(status)
.map(k => status[k].messages)
.reduce((r, m) => r.concat(m), [])
.filter(m => m.type > Severity.Info);
this.updateState({ errors });
});
}
private checkOutdated(): TPromise<void> {
return getOutdatedExtensions(this.extensionManagementService, this.extensionGalleryService)
.then(null, _ => []) // ignore errors
.then(outdated => {
this.updateState({ outdated });
// repeat this later
this.outdatedDelayer.trigger(() => this.checkOutdated());
});
}
}
}
@@ -5,19 +5,14 @@
import nls = require('vs/nls');
import errors = require('vs/base/common/errors');
import platform = require('vs/platform/platform');
import { Promise } from 'vs/base/common/winjs.base';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import Severity from 'vs/base/common/severity';
import { IWorkspaceContextService } from 'vs/workbench/services/workspace/common/contextService';
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
import wbaregistry = require('vs/workbench/common/actionRegistry');
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ListExtensionsAction, InstallExtensionAction, ListOutdatedExtensionsAction, ListSuggestedExtensionsAction } from './extensionsActions';
import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
import {ipcRenderer as ipc} from 'electron';
interface IInstallExtensionsRequest {
@@ -42,55 +37,8 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
this.install(options.extensionsToInstall).done(null, errors.onUnexpectedError);
}
const actionRegistry = (<wbaregistry.IWorkbenchActionRegistry> platform.Registry.as(wbaregistry.Extensions.WorkbenchActions));
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ListExtensionsAction, ListExtensionsAction.ID, ListExtensionsAction.LABEL), 'Extensions: Show Installed Extensions', ExtensionsLabel);
(<IQuickOpenRegistry>platform.Registry.as(Extensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen',
'LocalExtensionsHandler',
'ext ',
nls.localize('localExtensionsCommands', "Show Local Extensions")
)
);
if (galleryService.isEnabled()) {
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(InstallExtensionAction, InstallExtensionAction.ID, InstallExtensionAction.LABEL), 'Extensions: Install Extension', ExtensionsLabel);
(<IQuickOpenRegistry>platform.Registry.as(Extensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen',
'GalleryExtensionsHandler',
'ext install ',
nls.localize('galleryExtensionsCommands', "Install Gallery Extensions"),
true
)
);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ListOutdatedExtensionsAction, ListOutdatedExtensionsAction.ID, ListOutdatedExtensionsAction.LABEL), 'Extensions: Show Outdated Extensions', ExtensionsLabel);
(<IQuickOpenRegistry>platform.Registry.as(Extensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen',
'OutdatedExtensionsHandler',
'ext update ',
nls.localize('outdatedExtensionsCommands', "Update Outdated Extensions")
)
);
// add extension tips services
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ListSuggestedExtensionsAction, ListSuggestedExtensionsAction.ID, ListSuggestedExtensionsAction.LABEL), 'Extensions: Show Extension Recommendations', ExtensionsLabel);
(<IQuickOpenRegistry>platform.Registry.as(Extensions.Quickopen)).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen',
'SuggestedExtensionHandler',
'ext recommend ',
nls.localize('suggestedExtensionsCommands', "Show Extension Recommendations")
)
);
}
// const actionRegistry = (<wbaregistry.IWorkbenchActionRegistry> platform.Registry.as(wbaregistry.Extensions.WorkbenchActions));
// actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ListExtensionsAction, ListExtensionsAction.ID, ListExtensionsAction.LABEL), 'Extensions: Show Installed Extensions', ExtensionsLabel);
}
private registerListeners(): void {
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 10.3 10" style="enable-background:new 0 0 10.3 10;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{display:inline;opacity:0.2;fill:#ED1C24;}
.st2{display:inline;opacity:0.43;fill:#39B54A;}
.st3{fill:#353535;}
.st4{fill:#FFFFFF;}
.st5{display:inline;opacity:0.8;fill:#353535;}
.st6{display:inline;}
.st7{display:inline;opacity:0.8;fill:#321F5A;}
.st8{opacity:0.15;fill:#353535;}
.st9{display:inline;opacity:0.34;}
.st10{fill:#070909;}
.st11{fill:#333333;}
.st12{fill:#7551A0;}
.st13{fill:#D2D2D2;}
.st14{fill:#E6E7E8;}
.st15{fill:#3076BC;}
.st16{display:none;fill:#3076BC;}
.st17{display:inline;fill:#321F5A;}
.st18{fill:#D63F26;}
.st19{display:none;fill:none;stroke:#BCBEC0;stroke-miterlimit:10;}
.st20{opacity:0.52;}
.st21{fill:none;stroke:#BE1E2D;stroke-miterlimit:10;}
.st22{fill:#BE1E2D;}
.st23{display:none;opacity:0.2;}
.st24{display:inline;opacity:0.2;}
.st25{fill:#656565;}
.st26{fill:#D1D3D4;}
.st27{fill:#EEEEEE;}
.st28{display:none;fill-rule:evenodd;clip-rule:evenodd;fill:#F4F4F4;}
.st29{fill:#505150;}
.st30{display:none;fill:#BFD8E8;}
.st31{fill:#224096;}
.st32{fill:#17244E;}
.st33{display:inline;fill:#353535;}
.st34{display:none;fill:#F1F2F2;}
.st35{fill:#009444;}
.st36{fill:none;stroke:#505150;stroke-miterlimit:10;}
.st37{fill:#B9D532;}
.st38{fill:none;stroke:#D9D9D8;stroke-width:0.72;stroke-linejoin:round;stroke-miterlimit:10;}
.st39{fill:none;stroke:#D2D2D2;stroke-miterlimit:10;}
.st40{display:inline;fill:#333333;}
.st41{display:inline;fill:#7551A0;}
.st42{display:inline;fill:#D2D2D2;}
.st43{fill-rule:evenodd;clip-rule:evenodd;fill:#EEEEEE;}
.st44{fill:#321F5A;}
.st45{fill:none;stroke:#BCBEC0;stroke-miterlimit:10;}
.st46{opacity:0.53;fill:#FFFFFF;}
.st47{fill:none;stroke:#ED1C24;stroke-miterlimit:10;}
.st48{fill:#ED1C24;}
.st49{display:inline;opacity:0.1;fill:#E71E27;}
.st50{display:inline;opacity:0.3;}
.st51{fill:#E71E27;}
.st52{display:inline;opacity:0.2;fill:#E71E27;}
.st53{display:inline;fill:none;stroke:#ED1C24;stroke-miterlimit:10;}
.st54{fill:none;stroke:#2E3192;stroke-miterlimit:10;}
.st55{fill:#2E3192;}
.st56{fill:none;stroke:#FFF200;stroke-miterlimit:10;}
.st57{fill:#FFF200;}
</style>
<g id="grids">
</g>
<g id="Layer_2" class="st0">
</g>
<g id="home">
</g>
<g id="VSOdetails">
<g id="overview2">
</g>
</g>
<g id="reviews">
</g>
<g id="QA_2">
</g>
<g id="CARDS-RL">
<g>
<g>
<path class="st13" d="M8,9.7c-0.1,0-0.2,0-0.2-0.1L5.1,7.8L2.5,9.6c-0.1,0.1-0.3,0.1-0.5,0C1.9,9.5,1.9,9.4,1.9,9.2l1-3.1
L0.3,4.2C0.2,4.1,0.1,4,0.2,3.8c0.1-0.2,0.2-0.3,0.4-0.3h3.2l1-3.1C4.8,0.3,5,0.2,5.1,0.2c0.2,0,0.3,0.1,0.4,0.3l1,3.1h3.2
c0.2,0,0.3,0.1,0.4,0.3c0.1,0.2,0,0.3-0.1,0.5L7.4,6.1l1,3.1c0.1,0.2,0,0.3-0.1,0.5C8.2,9.7,8.1,9.7,8,9.7z"/>
</g>
</g>
</g>
<g id="sticky">
</g>
<g id="REDLINES" class="st0">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 10.3 10" style="enable-background:new 0 0 10.3 10;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{display:inline;opacity:0.2;fill:#ED1C24;}
.st2{display:inline;opacity:0.43;fill:#39B54A;}
.st3{fill:#353535;}
.st4{fill:#FFFFFF;}
.st5{display:inline;opacity:0.8;fill:#353535;}
.st6{display:inline;}
.st7{display:inline;opacity:0.8;fill:#321F5A;}
.st8{opacity:0.15;fill:#353535;}
.st9{display:inline;opacity:0.34;}
.st10{fill:#070909;}
.st11{fill:#333333;}
.st12{fill:#7551A0;}
.st13{fill:#D2D2D2;}
.st14{fill:#E6E7E8;}
.st15{fill:#3076BC;}
.st16{display:none;fill:#3076BC;}
.st17{display:inline;fill:#321F5A;}
.st18{fill:#FF8E00;}
.st19{display:none;fill:none;stroke:#BCBEC0;stroke-miterlimit:10;}
.st20{opacity:0.52;}
.st21{fill:none;stroke:#BE1E2D;stroke-miterlimit:10;}
.st22{fill:#BE1E2D;}
.st23{display:none;opacity:0.2;}
.st24{display:inline;opacity:0.2;}
.st25{fill:#656565;}
.st26{fill:#D1D3D4;}
.st27{fill:#EEEEEE;}
.st28{display:none;fill-rule:evenodd;clip-rule:evenodd;fill:#F4F4F4;}
.st29{fill:#505150;}
.st30{display:none;fill:#BFD8E8;}
.st31{fill:#224096;}
.st32{fill:#17244E;}
.st33{display:inline;fill:#353535;}
.st34{display:none;fill:#F1F2F2;}
.st35{fill:#009444;}
.st36{fill:none;stroke:#505150;stroke-miterlimit:10;}
.st37{fill:#B9D532;}
.st38{fill:none;stroke:#D9D9D8;stroke-width:0.72;stroke-linejoin:round;stroke-miterlimit:10;}
.st39{fill:none;stroke:#D2D2D2;stroke-miterlimit:10;}
.st40{display:inline;fill:#333333;}
.st41{display:inline;fill:#7551A0;}
.st42{display:inline;fill:#D2D2D2;}
.st43{fill-rule:evenodd;clip-rule:evenodd;fill:#EEEEEE;}
.st44{fill:#321F5A;}
.st45{fill:none;stroke:#BCBEC0;stroke-miterlimit:10;}
.st46{opacity:0.53;fill:#FFFFFF;}
.st47{fill:none;stroke:#ED1C24;stroke-miterlimit:10;}
.st48{fill:#ED1C24;}
.st49{display:inline;opacity:0.1;fill:#E71E27;}
.st50{display:inline;opacity:0.3;}
.st51{fill:#E71E27;}
.st52{display:inline;opacity:0.2;fill:#E71E27;}
.st53{display:inline;fill:none;stroke:#ED1C24;stroke-miterlimit:10;}
.st54{fill:none;stroke:#2E3192;stroke-miterlimit:10;}
.st55{fill:#2E3192;}
.st56{fill:none;stroke:#FFF200;stroke-miterlimit:10;}
.st57{fill:#FFF200;}
</style>
<g id="grids">
</g>
<g id="Layer_2" class="st0">
</g>
<g id="home">
</g>
<g id="VSOdetails">
<g id="overview2">
</g>
</g>
<g id="reviews">
</g>
<g id="QA_2">
</g>
<g id="CARDS-RL">
<g>
<g>
<path class="st18" d="M8,9.8c-0.1,0-0.2,0-0.2-0.1L5.2,7.8L2.6,9.7c-0.1,0.1-0.3,0.1-0.5,0C1.9,9.6,1.9,9.4,1.9,9.2l1-3.1
L0.3,4.3C0.2,4.2,0.1,4,0.2,3.8c0.1-0.2,0.2-0.3,0.4-0.3h3.2l1-3.1C4.8,0.3,5,0.2,5.2,0.2c0.2,0,0.3,0.1,0.4,0.3l1,3.1h3.2
c0.2,0,0.3,0.1,0.4,0.3c0.1,0.2,0,0.3-0.1,0.5L7.4,6.2l1,3.1c0.1,0.2,0,0.3-0.1,0.5C8.2,9.7,8.1,9.8,8,9.8z"/>
</g>
</g>
</g>
<g id="sticky">
</g>
<g id="REDLINES" class="st0">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 10.3 10" style="enable-background:new 0 0 10.3 10;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{display:inline;opacity:0.2;fill:#ED1C24;}
.st2{display:inline;opacity:0.43;fill:#39B54A;}
.st3{fill:#353535;}
.st4{fill:#FFFFFF;}
.st5{display:inline;opacity:0.8;fill:#353535;}
.st6{display:inline;}
.st7{display:inline;opacity:0.8;fill:#321F5A;}
.st8{opacity:0.15;fill:#353535;}
.st9{display:inline;opacity:0.34;}
.st10{fill:#070909;}
.st11{fill:#333333;}
.st12{fill:#7551A0;}
.st13{fill:#D2D2D2;}
.st14{fill:#E6E7E8;}
.st15{fill:#3076BC;}
.st16{display:none;fill:#3076BC;}
.st17{display:inline;fill:#321F5A;}
.st18{fill:#FF8E00;}
.st19{display:none;fill:none;stroke:#BCBEC0;stroke-miterlimit:10;}
.st20{opacity:0.52;}
.st21{fill:none;stroke:#BE1E2D;stroke-miterlimit:10;}
.st22{fill:#BE1E2D;}
.st23{display:none;opacity:0.2;}
.st24{display:inline;opacity:0.2;}
.st25{fill:#656565;}
.st26{fill:#D1D3D4;}
.st27{fill:#EEEEEE;}
.st28{display:none;fill-rule:evenodd;clip-rule:evenodd;fill:#F4F4F4;}
.st29{fill:#505150;}
.st30{display:none;fill:#BFD8E8;}
.st31{fill:#224096;}
.st32{fill:#17244E;}
.st33{display:inline;fill:#353535;}
.st34{display:none;fill:#F1F2F2;}
.st35{fill:#009444;}
.st36{fill:none;stroke:#505150;stroke-miterlimit:10;}
.st37{fill:#B9D532;}
.st38{fill:none;stroke:#D9D9D8;stroke-width:0.72;stroke-linejoin:round;stroke-miterlimit:10;}
.st39{fill:none;stroke:#D2D2D2;stroke-miterlimit:10;}
.st40{display:inline;fill:#333333;}
.st41{display:inline;fill:#7551A0;}
.st42{display:inline;fill:#D2D2D2;}
.st43{fill-rule:evenodd;clip-rule:evenodd;fill:#EEEEEE;}
.st44{fill:#321F5A;}
.st45{fill:none;stroke:#BCBEC0;stroke-miterlimit:10;}
.st46{opacity:0.53;fill:#FFFFFF;}
.st47{fill:none;stroke:#ED1C24;stroke-miterlimit:10;}
.st48{fill:#ED1C24;}
.st49{display:inline;opacity:0.1;fill:#E71E27;}
.st50{display:inline;opacity:0.3;}
.st51{fill:#E71E27;}
.st52{display:inline;opacity:0.2;fill:#E71E27;}
.st53{display:inline;fill:none;stroke:#ED1C24;stroke-miterlimit:10;}
.st54{fill:none;stroke:#2E3192;stroke-miterlimit:10;}
.st55{fill:#2E3192;}
.st56{fill:none;stroke:#FFF200;stroke-miterlimit:10;}
.st57{fill:#FFF200;}
</style>
<g id="grids">
</g>
<g id="Layer_2" class="st0">
</g>
<g id="home">
</g>
<g id="VSOdetails">
<g id="overview2">
</g>
</g>
<g id="reviews">
</g>
<g id="QA_2">
</g>
<g id="CARDS-RL">
<g>
<path class="st13" d="M10.1,3.8c-0.1-0.2-0.2-0.3-0.4-0.3H6.5l-1-3.1C5.5,0.3,5.3,0.2,5.2,0.2v7.6l2.6,1.9C7.8,9.7,7.9,9.7,8,9.7
s0.2,0,0.2-0.1c0.1-0.1,0.2-0.3,0.1-0.5l-1-3.1L10,4.2C10.1,4.1,10.2,4,10.1,3.8z"/>
<path class="st18" d="M5.2,0.2L5.2,0.2C5,0.2,4.8,0.3,4.8,0.5l-1,3.1H0.6c-0.2,0-0.3,0.1-0.4,0.3C0.1,4,0.2,4.1,0.3,4.2l2.6,1.9
l-1,3.1c-0.1,0.2,0,0.3,0.1,0.5c0.1,0.1,0.3,0.1,0.5,0l2.6-1.9l0,0V0.2z"/>
</g>
</g>
<g id="sticky">
</g>
<g id="REDLINES" class="st0">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-action-bar .action-item .action-label.extension-action {
border: 1px solid #CCC;
color: #6C6C6C;
background-color: #E2E2E2;
padding: 0 5px;
line-height: initial;
}
.monaco-action-bar .action-item:not(.disabled):hover .action-label.extension-action {
background-color: #D9D9D9;
}
.monaco-action-bar .action-item:not(.disabled):active .action-label.extension-action {
background-color: #C9C9C9;
}
.vs-dark .monaco-action-bar .action-item .action-label.extension-action {
border: 1px solid #545454;
color: #CCC;
background-color: #3A3A3A;
}
.vs-dark .monaco-action-bar .action-item:not(.disabled):hover .action-label.extension-action {
background-color: #464646;
}
.vs-dark .monaco-action-bar .action-item:not(.disabled):active .action-label.extension-action {
background-color: #505050;
}
.monaco-action-bar .action-item .action-label.extension-action.install,
.monaco-action-bar .action-item .action-label.extension-action.update {
color: white;
background-color: #327e36;
border-color: #519A55;
}
.monaco-action-bar .action-item:not(.disabled):hover .action-label.extension-action.install,
.monaco-action-bar .action-item:not(.disabled):hover .action-label.extension-action.update {
background-color: #478E4B;
}
.monaco-action-bar .action-item:not(.disabled):active .action-label.extension-action.install,
.monaco-action-bar .action-item:not(.disabled):active .action-label.extension-action.update {
background-color: #6DA770;
}
.monaco-action-bar .action-item.disabled .action-label.extension-action.update {
display: none;
}
@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.extension-editor {
height: 100%;
overflow-y: scroll;
display: flex;
flex-direction: column;
}
.extension-editor a {
color: inherit;
}
.extension-editor > .header {
display: flex;
height: 128px;
background: rgba(128, 128, 128, 0.15);
padding: 20px;
overflow: hidden;
font-size: 14px;
}
.extension-editor > .header > .icon {
height: 128px;
width: 128px;
min-width: 128px;
background-size: 128px;
background-repeat: no-repeat;
background-position: center center;
}
.extension-editor > .header > .details {
flex: 1;
padding-left: 20px;
overflow: hidden;
}
.extension-editor > .header > .details > .name {
font-size: 26px;
font-weight: 600;
line-height: normal;
white-space: nowrap;
}
.extension-editor > .header > .details > .subtitle {
padding-top: 10px;
white-space: nowrap;
height: 20px;
line-height: 20px;
}
.extension-editor > .header > .details > .subtitle > .publisher {
font-size: 18px;
}
.extension-editor > .header > .details > .subtitle > .install > .count {
margin-left: 6px;
}
.extension-editor > .header > .details > .subtitle > span:not(:first-child):not(:empty),
.extension-editor > .header > .details > .subtitle > a:not(:first-child):not(:empty) {
border-left: 1px solid rgba(128, 128, 128, 0.7);
margin-left: 14px;
padding-left: 14px;
}
.extension-editor > .header > .details > .description {
margin-top: 14px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.extension-editor > .header > .details > .actions {
margin-top: 14px;
}
.extension-editor > .header > .details > .actions > .monaco-action-bar {
text-align: initial;
}
.extension-editor > .header > .details > .actions > .monaco-action-bar > .actions-container {
justify-content: flex-start;
}
.extension-editor > .body {
flex: 1;
overflow-y: scroll;
overflow-x: hidden;
padding: 20px;
}
.extension-editor > .body img {
max-width: 100%;
}
.extension-editor > .body.loading {
background-image: url('loading.svg');
background-position: center center;
background-repeat: no-repeat;
}
@@ -198,4 +198,13 @@
background-size: 8px;
background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.5) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.5) 75%, transparent 75%, transparent);
animation: move-background 0.5s linear infinite;
}
/* Global action */
.monaco-workbench > .activitybar .monaco-action-bar .action-label.extensions {
background: url('extensions-status.svg');
background-size: 22px;
background-repeat: no-repeat;
background-position: 50% !important;
}
@@ -0,0 +1,96 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.extensions-viewlet {
height: 100%;
}
.extensions-viewlet > .header {
height: 38px;
box-sizing: border-box;
padding: 5px 9px 5px 16px;
}
.extensions-viewlet > .header > .search-box {
width: 100%;
height: 26px;
box-sizing: border-box;
padding: 0 3px;
}
.extensions-viewlet > .extensions {
height: calc(100% - 38px);
}
.extensions-viewlet > .extensions .extension {
box-sizing: border-box;
width: 100%;
height: 100%;
padding: 0 19px 0 16px;
overflow: hidden;
display: flex;
}
.extensions-viewlet > .extensions .extension.loading {
background-image: url('loading.svg');
background-position: center center;
background-repeat: no-repeat;
}
.extensions-viewlet > .extensions .extension > .icon {
width: 42px;
height: 42px;
padding: 10px 14px 10px 0;
flex-shrink: 0;
background-repeat: no-repeat;
background-size: 42px;
background-position: left center;
}
.extensions-viewlet > .extensions .extension.loading > .icon {
display: none;
}
.extensions-viewlet > .extensions .extension > .details {
flex: 1;
padding: 4px 0;
overflow: hidden;
}
.extensions-viewlet > .extensions .extension > .details > .header {
display: flex;
}
.extensions-viewlet > .extensions .extension > .details > .header > .name {
font-weight: bold;
}
.extensions-viewlet > .extensions .extension > .details > .header > .version {
opacity: 0.6;
font-size: 80%;
padding-left: 6px;
}
.extensions-viewlet > .extensions .extension > .details > .header > .author {
flex: 1;
font-size: 90%;
text-align: right;
padding-left: 6px;
}
.extensions-viewlet > .extensions .extension > .details > .monaco-action-bar .action-label {
margin-right: 0;
margin-left: 0.3em;
}
.extensions-viewlet > .extensions .extension .ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/*.extensions-viewlet > .extensions .extension > .details {
width: 100%;
}*/
@@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.extension-ratings {
display: inline-block;
}
.extension-ratings > .star {
display: inline-block;
width: 16px;
height: 16px;
background-repeat: no-repeat;
background-position: center center;
}
.extension-ratings > .star:not(:first-child) {
margin-left: 3px;
}
.extension-ratings.small > .star {
width: 12px;
height: 12px;
}
.extension-ratings > .full {
background-image: url('./FullStarLight.svg');
}
.extension-ratings > .half {
background-image: url('./HalfStarLight.svg');
}
.extension-ratings > .empty {
background-image: url('./EmptyStar.svg');
}
.extension-ratings > .count {
margin-left: 6px;
}
-1
View File
@@ -47,7 +47,6 @@ import 'vs/workbench/parts/markers/markers.contribution';
import 'vs/workbench/parts/html/browser/html.contribution';
import 'vs/workbench/parts/extensions/electron-browser/extensions.contribution';
import 'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen';
import 'vs/workbench/parts/output/browser/output.contribution';