mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-26 18:06:43 +01:00
Merge branch 'master' into joh/menus
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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>(<)</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>meta.brace.angle.js</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>(?=$)|(>)</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$]*)?(<)</string>
|
||||
<string>([a-zA-Z_$][\w$]*)?\s*(<)(?=[^<]*(<[^<>]*>)*>\s*[(])</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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,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>(<)</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>meta.brace.angle.ts</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>(?=$)|(>)</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$]*)?(<)</string>
|
||||
<string>([a-zA-Z_$][\w$]*)?\s*(<)(?=[^<]*(<[^<>]*>)*>\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>(<)</string>
|
||||
<key>beginCaptures</key>
|
||||
<dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>meta.brace.angle.tsx</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>end</key>
|
||||
<string>(?=$)|(>)</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$]*)?(<)</string>
|
||||
<string>([a-zA-Z_$][\w$]*)?\s*(<)(?=[^<]*(<[^<>]*>)*>\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
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
Vendored
+11
-9
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
});
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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' }
|
||||
]}]
|
||||
]);
|
||||
});
|
||||
});
|
||||
Vendored
+24
-11
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
Vendored
+1
-1
@@ -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());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user