提交 97f5c112 编写于 作者: D Dirk Baeumer

Merge branch 'master' into dbaeumer/TS2.0

......@@ -38,7 +38,8 @@ const nodeModules = ['electron', 'original-fs']
// Build
const builtInExtensions = [
{ name: 'ms-vscode.node-debug', version: '1.6.2' }
{ name: 'ms-vscode.node-debug', version: '1.6.5' },
{ name: 'ms-vscode.node-debug2', version: '0.0.1' }
];
const vscodeEntryPoints = _.flatten([
......
......@@ -151,14 +151,14 @@ gulp.task('clean-vscode-linux-arm-rpm', util.rimraf('.build/linux/rpm/armhf'));
gulp.task('vscode-linux-ia32-prepare-deb', ['clean-vscode-linux-ia32-deb', 'vscode-linux-ia32-min'], prepareDebPackage('ia32'));
gulp.task('vscode-linux-x64-prepare-deb', ['clean-vscode-linux-x64-deb', 'vscode-linux-x64-min'], prepareDebPackage('x64'));
gulp.task('vscode-linux-arm-prepare-deb', ['clean-vscode-linux-arm-deb', 'vscode-linux-arm-min'], prepareDebPackage('armhf'));
gulp.task('vscode-linux-arm-prepare-deb', ['clean-vscode-linux-arm-deb', 'vscode-linux-arm-min'], prepareDebPackage('arm'));
gulp.task('vscode-linux-ia32-build-deb', ['vscode-linux-ia32-prepare-deb'], buildDebPackage('ia32'));
gulp.task('vscode-linux-x64-build-deb', ['vscode-linux-x64-prepare-deb'], buildDebPackage('x64'));
gulp.task('vscode-linux-arm-build-deb', ['vscode-linux-arm-prepare-deb'], buildDebPackage('armhf'));
gulp.task('vscode-linux-arm-build-deb', ['vscode-linux-arm-prepare-deb'], buildDebPackage('arm'));
gulp.task('vscode-linux-ia32-prepare-rpm', ['clean-vscode-linux-ia32-rpm', 'vscode-linux-ia32-min'], prepareRpmPackage('ia32'));
gulp.task('vscode-linux-x64-prepare-rpm', ['clean-vscode-linux-x64-rpm', 'vscode-linux-x64-min'], prepareRpmPackage('x64'));
gulp.task('vscode-linux-arm-prepare-rpm', ['clean-vscode-linux-arm-rpm', 'vscode-linux-arm-min'], prepareRpmPackage('armhf'));
gulp.task('vscode-linux-arm-prepare-rpm', ['clean-vscode-linux-arm-rpm', 'vscode-linux-arm-min'], prepareRpmPackage('arm'));
gulp.task('vscode-linux-ia32-build-rpm', ['vscode-linux-ia32-prepare-rpm'], buildRpmPackage('ia32'));
gulp.task('vscode-linux-x64-build-rpm', ['vscode-linux-x64-prepare-rpm'], buildRpmPackage('x64'));
gulp.task('vscode-linux-arm-build-rpm', ['vscode-linux-arm-prepare-rpm'], buildRpmPackage('armhf'));
gulp.task('vscode-linux-arm-build-rpm', ['vscode-linux-arm-prepare-rpm'], buildRpmPackage('arm'));
......@@ -6,7 +6,6 @@
'use strict';
import * as nls from 'vs/nls';
import * as fs from 'original-fs';
import { app, ipcMain as ipc } from 'electron';
import { assign } from 'vs/base/common/objects';
import * as platform from 'vs/base/common/platform';
......@@ -39,11 +38,15 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/node/requestService';
import * as cp from 'child_process';
import { generateUuid } from 'vs/base/common/uuid';
import { getPathLabel } from 'vs/base/common/labels';
import { URLChannel } from 'vs/platform/url/common/urlIpc';
import { URLService } from 'vs/platform/url/electron-main/urlService';
import * as fs from 'original-fs';
import * as cp from 'child_process';
import * as path from 'path';
function quit(accessor: ServicesAccessor, error?: Error);
function quit(accessor: ServicesAccessor, message?: string);
function quit(accessor: ServicesAccessor, arg?: any) {
......@@ -193,25 +196,54 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: IProce
// Install JumpList on Windows
if (platform.isWindows) {
app.setJumpList([
{
type: 'tasks',
items: [
{
const jumpList: Electron.JumpListCategory[] = [];
// Tasks
jumpList.push({
type: 'tasks',
items: [
{
type: 'task',
title: nls.localize('newWindow', "New Window"),
description: nls.localize('newWindowDesc', "Opens a new window"),
program: process.execPath,
args: '-n', // force new window
iconPath: process.execPath,
iconIndex: 0
}
]
});
// Recent Folders
const folders = windowsService.getRecentPathsList().folders;
if (folders.length > 0) {
jumpList.push({
type: 'custom',
name: 'Recent Folders',
items: windowsService.getRecentPathsList().folders.slice(0, 7 /* limit number of entries here */).map(folder => {
return <Electron.JumpListItem>{
type: 'task',
title: nls.localize('newWindow', "New Window"),
description: nls.localize('newWindowDesc', "Opens a new window"),
title: getPathLabel(folder),
description: nls.localize('folderDesc', "{0} {1}", path.basename(folder), getPathLabel(path.dirname(folder))),
program: process.execPath,
args: '-n', // force new window
args: folder, // open folder,
iconPath: process.execPath,
iconIndex: 0
}
]
},
{
type: 'recent' // this enables to show files in the "recent" category
}
]);
};
})
});
}
// Recent
jumpList.push({
type: 'recent' // this enables to show files in the "recent" category
});
try {
app.setJumpList(jumpList);
} catch (error) {
logService.log('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors
}
}
// Setup auto update
......
......@@ -103,6 +103,10 @@ export interface IExtensionIdentity {
publisher: string;
}
export interface IGalleryExtensionProperties {
dependencies?: string[];
}
export interface IGalleryExtensionAssets {
manifest: string;
readme: string;
......@@ -126,6 +130,7 @@ export interface IGalleryExtension {
rating: number;
ratingCount: number;
assets: IGalleryExtensionAssets;
properties: IGalleryExtensionProperties;
downloadHeaders: { [key: string]: string; };
}
......@@ -184,6 +189,8 @@ export interface IExtensionGalleryService {
query(options?: IQueryOptions): TPromise<IPager<IGalleryExtension>>;
download(extension: IGalleryExtension): TPromise<string>;
getAsset(url: string): TPromise<IRequestContext>;
loadCompatibleVersion(extension: IGalleryExtension): TPromise<IGalleryExtension>;
getAllDependencies(extension: IGalleryExtension): TPromise<IGalleryExtension[]>;
}
export interface InstallExtensionEvent {
......
......@@ -7,6 +7,8 @@ import { localize } from 'vs/nls';
import { tmpdir } from 'os';
import * as path from 'path';
import { TPromise } from 'vs/base/common/winjs.base';
import { distinct } from 'vs/base/common/arrays';
import { ArraySet } from 'vs/base/common/set';
import { IGalleryExtension, IExtensionGalleryService, IQueryOptions, SortBy, SortOrder, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionTelemetry';
import { isUndefined } from 'vs/base/common/types';
......@@ -26,11 +28,17 @@ interface IRawGalleryExtensionFile {
source: string;
}
interface IRawGalleryExtensionProperty {
key: string;
value: string;
}
interface IRawGalleryExtensionVersion {
version: string;
lastUpdated: string;
assetUri: string;
files: IRawGalleryExtensionFile[];
properties: IRawGalleryExtensionProperty[];
}
interface IRawGalleryExtensionStatistics {
......@@ -90,7 +98,11 @@ const AssetType = {
Details: 'Microsoft.VisualStudio.Services.Content.Details',
Manifest: 'Microsoft.VisualStudio.Code.Manifest',
VSIX: 'Microsoft.VisualStudio.Services.VSIXPackage',
License: 'Microsoft.VisualStudio.Services.Content.License'
License: 'Microsoft.VisualStudio.Services.Content.License',
};
const PropertyType = {
Dependency: 'Microsoft.VisualStudio.Code.ExtensionDependencies'
};
interface ICriterium {
......@@ -178,7 +190,13 @@ function getAssetSource(files: IRawGalleryExtensionFile[], type: string): string
const result = files.filter(f => f.assetType === type)[0];
return result && result.source;
}
function getDependencies(properties: IRawGalleryExtensionProperty[]): string[] {
const values = properties.filter(p => p.key === PropertyType.Dependency);
if (values.length && values[0].value) {
return values[0].value.split(',');
}
return [];
}
function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string, downloadHeaders: { [key: string]: string; }): IGalleryExtension {
const [version] = galleryExtension.versions;
......@@ -203,6 +221,8 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
license: getAssetSource(version.files, AssetType.License)
};
const dependencies = version.properties ? getDependencies(version.properties) : void 0;
return {
id: galleryExtension.extensionId,
name: galleryExtension.extensionName,
......@@ -217,6 +237,9 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
rating: getStatistic(galleryExtension.statistics, 'averagerating'),
ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'),
assets,
properties: {
dependencies
},
downloadHeaders
};
}
......@@ -337,8 +360,35 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
download(extension: IGalleryExtension): TPromise<string> {
return this.loadCompatibleVersion(extension).then(extension => {
const url = extension.assets.download;
const zipPath = path.join(tmpdir(), extension.id);
const data = getGalleryExtensionTelemetryData(extension);
const startTime = new Date().getTime();
const log = duration => this.telemetryService.publicLog('galleryService:downloadVSIX', assign(data, { duration }));
return this.getCommonHeaders()
.then(headers => this._getAsset({ url, headers }))
.then(context => download(zipPath, context))
.then(() => log(new Date().getTime() - startTime))
.then(() => zipPath);
});
}
getAsset(url: string): TPromise<IRequestContext> {
return this._getAsset({ url });
}
getAllDependencies(extension: IGalleryExtension): TPromise<IGalleryExtension[]> {
return this.loadCompatibleVersion(<IGalleryExtension>extension)
.then(compatible => this.getDependenciesReccursively(compatible.properties.dependencies, [extension]))
.then(dependencies => dependencies.slice(1));
}
loadCompatibleVersion(extension: IGalleryExtension): TPromise<IGalleryExtension> {
// TODO:sandy: Check if the given version is compatible
const query = new Query()
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles)
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, 1)
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
.withAssetTypes(AssetType.Manifest, AssetType.VSIX)
......@@ -346,29 +396,64 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return this.queryGallery(query).then(({ galleryExtensions }) => {
const [rawExtension] = galleryExtensions;
if (!rawExtension) {
return TPromise.wrapError(new Error(localize('notFound', "Extension not found")));
}
return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions).then(rawVersion => {
const url = `${ getAssetSource(rawVersion.files, AssetType.VSIX) }?install=true`;
const zipPath = path.join(tmpdir(), extension.id);
const data = getGalleryExtensionTelemetryData(extension);
const startTime = new Date().getTime();
const log = duration => this.telemetryService.publicLog('galleryService:downloadVSIX', assign(data, { duration }));
return this.getCommonHeaders()
.then(headers => this._getAsset({ url, headers }))
.then(context => download(zipPath, context))
.then(() => log(new Date().getTime() - startTime))
.then(() => zipPath);
});
return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions)
.then(rawVersion => {
extension.properties.dependencies = getDependencies(rawVersion.properties);
extension.assets.download = `${ getAssetSource(rawVersion.files, AssetType.VSIX) }?install=true`;
return extension;
});
});
}
getAsset(url: string): TPromise<IRequestContext> {
return this._getAsset({ url });
private loadDependencies(extensionNames: string[]): TPromise<IGalleryExtension[]> {
let query = new Query()
.withFlags(Flags.IncludeVersions, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties)
.withPage(1, extensionNames.length)
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
.withAssetTypes(AssetType.Icon, AssetType.License, AssetType.Details, AssetType.Manifest, AssetType.VSIX);
query = extensionNames.reduce((query, name) => query.withFilter(FilterType.ExtensionName, name), query);
return this.queryGallery(query)
.then(result => this.getCommonHeaders()
.then(downloadHeaders => {
const dependencies = [];
const ids = [];
for (const galleryExtension of result.galleryExtensions) {
if (ids.indexOf(galleryExtension.extensionId) === -1) {
dependencies.push(toExtension(galleryExtension, this.extensionsGalleryUrl, downloadHeaders));
ids.push(galleryExtension.extensionId);
}
}
return dependencies;
})
);
}
private getDependenciesReccursively(toGet: string[], result: IGalleryExtension[]): TPromise<IGalleryExtension[]> {
if (!toGet || !toGet.length) {
return TPromise.wrap(result);
}
toGet = result.length ? toGet.filter(e => !ExtensionGalleryService.hasExtensionByName(result, e)) : toGet;
if (!toGet.length) {
return TPromise.wrap(result);
}
return this.loadDependencies(toGet)
.then(loadedDependencies => {
const dependenciesSet = new ArraySet<string>();
for (const dep of loadedDependencies) {
if (dep.properties.dependencies) {
dep.properties.dependencies.forEach(d => dependenciesSet.set(d));
}
}
result = distinct(result.concat(loadedDependencies), d => d.id);
const dependencies = dependenciesSet.elements.filter(d => !ExtensionGalleryService.hasExtensionByName(result, d));
return this.getDependenciesReccursively(dependencies, result);
});
}
/**
......@@ -416,4 +501,13 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
return version;
});
}
private static hasExtensionByName(extensions: IGalleryExtension[], name: string): boolean {
for (const extension of extensions) {
if (`${extension.publisher}.${extension.name}` === name) {
return true;
}
}
return false;
}
}
\ No newline at end of file
......@@ -8,6 +8,7 @@
import nls = require('vs/nls');
import * as path from 'path';
import * as pfs from 'vs/base/node/pfs';
import * as errors from 'vs/base/common/errors';
import { assign } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { flatten } from 'vs/base/common/arrays';
......@@ -24,6 +25,7 @@ import Event, { Emitter } from 'vs/base/common/event';
import * as semver from 'semver';
import { groupBy, values } from 'vs/base/common/collections';
import URI from 'vs/base/common/uri';
import { IChoiceService, Severity } from 'vs/platform/message/common/message';
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
......@@ -106,6 +108,7 @@ export class ExtensionManagementService implements IExtensionManagementService {
constructor(
@IEnvironmentService private environmentService: IEnvironmentService,
@IChoiceService private choiceService: IChoiceService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService
) {
this.extensionsPath = environmentService.extensionsPath;
......@@ -135,30 +138,97 @@ export class ExtensionManagementService implements IExtensionManagementService {
});
}
installFromGallery(extension: IGalleryExtension): TPromise<void> {
installFromGallery(extension: IGalleryExtension, checkDependecies: boolean = true): TPromise<void> {
const id = getExtensionId(extension, extension.version);
return this.isObsolete(id).then(isObsolete => {
if (isObsolete) {
return TPromise.wrapError<void>(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", extension.displayName || extension.name)));
}
this._onInstallExtension.fire({ id, gallery: extension });
return this.galleryService.loadCompatibleVersion(extension)
.then(compatibleVersion => this.installCompatibleVersion(compatibleVersion, checkDependecies))
.then(
local => this._onDidInstallExtension.fire({ id, local, gallery: extension }),
error => {
this._onDidInstallExtension.fire({ id, gallery: extension, error });
return TPromise.wrapError(error);
}
);
});
}
private installCompatibleVersion(extension: IGalleryExtension, checkDependecies: boolean): TPromise<ILocalExtension> {
const dependencies = checkDependecies ? this.getDependenciesToInstall(extension) : [];
if (!dependencies.length) {
return this.downloadAndInstall(extension);
}
const message = nls.localize('installDependecies', "This extension has dependencies. Would you like to install them along with it?");
const options = [
nls.localize('installWithDependenices', "Install With Dependencies"),
nls.localize('installWithoutDependenices', "Install only this"),
nls.localize('close', "Close")
];
return this.choiceService.choose(Severity.Info, message, options)
.then(value => {
switch (value) {
case 0:
return this.installWithDependencies(extension);
case 1:
return this.downloadAndInstall(extension);
default:
return TPromise.wrapError(errors.canceled());
}
});
}
private getDependenciesToInstall(extension: IGalleryExtension): string[] {
const extensionName = `${extension.publisher}.${extension.name}`;
return extension.properties.dependencies ? extension.properties.dependencies.filter(name => name !== extensionName) : [];
}
private installWithDependencies(extension: IGalleryExtension): TPromise<ILocalExtension> {
return this.galleryService.getAllDependencies(extension)
.then(allDependencies => this.filterOutInstalled(allDependencies))
.then(toInstall => this.bulkInstallWithDependencies(extension, toInstall));
}
private bulkInstallWithDependencies(extension: IGalleryExtension, dependecies: IGalleryExtension[]): TPromise<ILocalExtension> {
return this.downloadAndInstall(extension)
.then(localExtension => TPromise.join(dependecies.map((dep) => this.installFromGallery(dep, false)))
.then(() => localExtension, error => this.rollback(localExtension, dependecies).then(() => error)));
}
private rollback(localExtension: ILocalExtension, dependecies: IGalleryExtension[]): TPromise<void> {
return this.uninstall(localExtension)
.then(() => this.filterOutUnInstalled(dependecies))
.then(installed => TPromise.join(installed.map((i) => this.uninstall(i))))
.then(() => null);
}
private filterOutInstalled(extensions: IGalleryExtension[]): TPromise<IGalleryExtension[]> {
return this.getInstalled().then(local => {
return extensions.filter(extension => local.every(local => local.id !== extension.id));
});
}
private filterOutUnInstalled(extensions: IGalleryExtension[]): TPromise<ILocalExtension[]> {
return this.getInstalled().then(installed => {
return installed.filter(local => extensions.every(extension => extension.id === local.id));
});
}
const metadata = {
private downloadAndInstall(extension: IGalleryExtension): TPromise<ILocalExtension> {
const id = getExtensionId(extension, extension.version);
const metadata = {
id: extension.id,
publisherId: extension.publisherId,
publisherDisplayName: extension.publisherDisplayName
};
return this.galleryService.download(extension)
.then(zipPath => validate(zipPath).then(() => zipPath))
.then(zipPath => this.installExtension(zipPath, id, metadata))
.then(
local => this._onDidInstallExtension.fire({ id, local, gallery: extension }),
error => { this._onDidInstallExtension.fire({ id, gallery: extension, error }); return TPromise.wrapError(error); }
);
});
return this.galleryService.download(extension)
.then(zipPath => validate(zipPath).then(() => zipPath))
.then(zipPath => this.installExtension(zipPath, id, metadata));
}
private installExtension(zipPath: string, id: string, metadata: IGalleryMetadata = null): TPromise<ILocalExtension> {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册