提交 868cde2e 编写于 作者: S SteVen Batten

Merge branch 'master' of https://github.com/Microsoft/vscode

{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Grammar",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}"
]
}
]
}
......@@ -14,6 +14,7 @@ import * as languageIds from '../utils/languageModeIds';
import * as typeConverters from '../utils/typeConverters';
import FileConfigurationManager from './fileConfigurationManager';
import * as fileSchemes from '../utils/fileSchemes';
import { escapeRegExp } from '../utils/regexp';
const localize = nls.loadMessageBundle();
......@@ -222,29 +223,45 @@ export class UpdateImportsOnFileRenameHandler {
return;
}
return typeConverters.WorkspaceEdit.fromFromFileCodeEdits(this.client, response.body.map((edit: Proto.FileCodeEdits) => this.fixEdit(edit, isDirectoryRename)));
const edits: Proto.FileCodeEdits[] = [];
for (const edit of response.body) {
edits.push(await this.fixEdit(edit, isDirectoryRename, oldFile, newFile));
}
return typeConverters.WorkspaceEdit.fromFromFileCodeEdits(this.client, edits);
}
private fixEdit(
private async fixEdit(
edit: Proto.FileCodeEdits,
isDirectoryRename: boolean
): Proto.FileCodeEdits {
isDirectoryRename: boolean,
oldFile: string,
newFile: string,
): Promise<Proto.FileCodeEdits> {
if (!isDirectoryRename || this.client.apiVersion.gte(API.v300)) {
return edit;
}
const document = await vscode.workspace.openTextDocument(edit.fileName);
const oldFileRe = new RegExp('/' + escapeRegExp(path.basename(oldFile)) + '/');
// Workaround for https://github.com/Microsoft/TypeScript/issues/24968
const textChanges = edit.textChanges.map((change): Proto.CodeEdit => {
const match = change.newText.match(/\/[^\/]+$/g);
const existingText = document.getText(typeConverters.Range.fromTextSpan(change));
const existingMatch = existingText.match(oldFileRe);
if (!existingMatch) {
return change;
}
const match = new RegExp('/' + escapeRegExp(path.basename(newFile)) + '/(.+)$', 'g').exec(change.newText);
if (!match) {
return change;
}
return {
newText: change.newText.slice(0, -match[0].length),
newText: change.newText.slice(0, -match[1].length),
start: change.start,
end: {
line: change.end.line,
offset: change.end.offset - match[0].length
offset: change.end.offset - match[1].length
}
};
});
......
......@@ -3,6 +3,39 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
const fs = require('fs');
const path = require('path');
const product = require('../product.json');
const appRoot = path.dirname(__dirname);
function getApplicationPath() {
if (process.env['VSCODE_DEV']) {
return appRoot;
} else if (process.platform === 'darwin') {
return path.dirname(path.dirname(path.dirname(appRoot)));
} else {
return path.dirname(path.dirname(appRoot));
}
}
const portableDataName = product.portable || `${product.applicationName}-portable-data`;
const portableDataPath = path.join(path.dirname(getApplicationPath()), portableDataName);
const isPortable = fs.existsSync(portableDataPath);
const portableTempPath = path.join(portableDataPath, 'tmp');
const isTempPortable = isPortable && fs.existsSync(portableTempPath);
if (isPortable) {
process.env['VSCODE_PORTABLE'] = portableDataPath;
} else {
delete process.env['VSCODE_PORTABLE'];
}
if (isTempPortable) {
process.env[process.platform === 'win32' ? 'TEMP' : 'TMPDIR'] = portableTempPath;
}
//#region Add support for using node_modules.asar
(function () {
const path = require('path');
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
let perf = require('./vs/base/common/performance');
const perf = require('./vs/base/common/performance');
perf.mark('main:started');
// Perf measurements
......@@ -12,6 +12,37 @@ global.perfStartTime = Date.now();
Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
const fs = require('fs');
const path = require('path');
const product = require('../product.json');
const appRoot = path.dirname(__dirname);
function getApplicationPath() {
if (process.env['VSCODE_DEV']) {
return appRoot;
} else if (process.platform === 'darwin') {
return path.dirname(path.dirname(path.dirname(appRoot)));
} else {
return path.dirname(path.dirname(appRoot));
}
}
const portableDataName = product.portable || `${product.applicationName}-portable-data`;
const portableDataPath = process.env['VSCODE_PORTABLE'] || path.join(path.dirname(getApplicationPath()), portableDataName);
const isPortable = fs.existsSync(portableDataPath);
const portableTempPath = path.join(portableDataPath, 'tmp');
const isTempPortable = isPortable && fs.existsSync(portableTempPath);
if (isPortable) {
process.env['VSCODE_PORTABLE'] = portableDataPath;
} else {
delete process.env['VSCODE_PORTABLE'];
}
if (isTempPortable) {
process.env[process.platform === 'win32' ? 'TEMP' : 'TMPDIR'] = portableTempPath;
}
//#region Add support for using node_modules.asar
(function () {
const path = require('path');
......@@ -36,7 +67,7 @@ Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https:
})();
//#endregion
let app = require('electron').app;
const app = require('electron').app;
// TODO@Ben Electron 2.0.x: prevent localStorage migration from SQLite to LevelDB due to issues
app.commandLine.appendSwitch('disable-mojo-local-storage');
......@@ -44,15 +75,19 @@ app.commandLine.appendSwitch('disable-mojo-local-storage');
// TODO@Ben Electron 2.0.x: force srgb color profile (for https://github.com/Microsoft/vscode/issues/51791)
app.commandLine.appendSwitch('force-color-profile', 'srgb');
let fs = require('fs');
let path = require('path');
let minimist = require('minimist');
let paths = require('./paths');
const minimist = require('minimist');
const paths = require('./paths');
let args = minimist(process.argv, {
string: ['user-data-dir', 'locale']
const args = minimist(process.argv, {
string: [
'user-data-dir',
'locale',
'js-flags',
'max-memory'
]
});
//#region NLS
function stripComments(content) {
let regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
let result = content.replace(regexp, function (match, m1, m2, m3, m4) {
......@@ -77,113 +112,38 @@ function stripComments(content) {
return result;
}
let _commit;
function getCommit() {
if (_commit) {
return _commit;
}
if (_commit === null) {
return undefined;
}
try {
let productJson = require(path.join(__dirname, '../product.json'));
if (productJson.commit) {
_commit = productJson.commit;
} else {
_commit = null;
}
} catch (exp) {
_commit = null;
}
return _commit || undefined;
}
const mkdir = dir => new Promise((c, e) => fs.mkdir(dir, err => (err && err.code !== 'EEXIST') ? e(err) : c()));
const exists = file => new Promise(c => fs.exists(file, c));
const readFile = file => new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data)));
const writeFile = (file, content) => new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c()));
const touch = file => new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); });
function mkdirp(dir) {
return mkdir(dir)
.then(null, (err) => {
if (err && err.code === 'ENOENT') {
let parent = path.dirname(dir);
if (parent !== dir) { // if not arrived at root
return mkdirp(parent)
.then(() => {
return mkdir(dir);
});
}
}
throw err;
});
}
function mkdir(dir) {
return new Promise((resolve, reject) => {
fs.mkdir(dir, (err) => {
if (err && err.code !== 'EEXIST') {
reject(err);
} else {
resolve(dir);
}
});
});
}
function exists(file) {
return new Promise((resolve) => {
fs.exists(file, (result) => {
resolve(result);
});
});
}
function readFile(file) {
return new Promise((resolve, reject) => {
fs.readFile(file, 'utf8', (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}
return mkdir(dir).then(null, err => {
if (err && err.code === 'ENOENT') {
const parent = path.dirname(dir);
function writeFile(file, content) {
return new Promise((resolve, reject) => {
fs.writeFile(file, content, 'utf8', (err) => {
if (err) {
reject(err);
return;
if (parent !== dir) { // if not arrived at root
return mkdirp(parent).then(() => mkdir(dir));
}
resolve(undefined);
});
});
}
}
function touch(file) {
return new Promise((resolve, reject) => {
let d = new Date();
fs.utimes(file, d, d, (err) => {
if (err) {
reject(err);
return;
}
resolve(undefined);
});
throw err;
});
}
function resolveJSFlags() {
let jsFlags = [];
const jsFlags = [];
if (args['js-flags']) {
jsFlags.push(args['js-flags']);
}
if (args['max-memory'] && !/max_old_space_size=(\d+)/g.exec(args['js-flags'])) {
jsFlags.push(`--max_old_space_size=${args['max-memory']}`);
}
if (jsFlags.length > 0) {
return jsFlags.join(' ');
} else {
return null;
}
return jsFlags.length > 0 ? jsFlags.join(' ') : null;
}
// Language tags are case insensitve however an amd loader is case sensitive
......@@ -288,7 +248,7 @@ function getNLSConfiguration(locale) {
}
perf.mark('nlsGeneration:start');
let defaultResult = function(locale) {
let defaultResult = function (locale) {
let isCoreLanguage = true;
if (locale) {
isCoreLanguage = ['de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-cn', 'zh-tw'].some((language) => {
......@@ -299,13 +259,13 @@ function getNLSConfiguration(locale) {
let result = resolveLocale(locale);
perf.mark('nlsGeneration:end');
return Promise.resolve(result);
} else {
} else {
perf.mark('nlsGeneration:end');
return Promise.resolve({ locale: locale, availableLanguages: {} });
}
};
try {
let commit = getCommit();
let commit = product.commit;
if (!commit) {
return defaultResult(locale);
}
......@@ -342,7 +302,7 @@ function getNLSConfiguration(locale) {
return exists(coreLocation).then((fileExists) => {
if (fileExists) {
// We don't wait for this. No big harm if we can't touch
touch(coreLocation).catch(() => {});
touch(coreLocation).catch(() => { });
perf.mark('nlsGeneration:end');
return result;
}
......@@ -395,7 +355,9 @@ function getNLSConfiguration(locale) {
return defaultResult(locale);
}
}
//#endregion
//#region Cached Data Dir
function getNodeCachedDataDir() {
// flag to disable cached data support
if (process.argv.indexOf('--no-cached-data') > 0) {
......@@ -408,7 +370,7 @@ function getNodeCachedDataDir() {
}
// find commit id
let commit = getCommit();
let commit = product.commit;
if (!commit) {
return Promise.resolve(undefined);
}
......@@ -417,10 +379,18 @@ function getNodeCachedDataDir() {
return mkdirp(dir).then(undefined, function () { /*ignore*/ });
}
//#endregion
function getUserDataPath() {
if (isPortable) {
return path.join(portableDataPath, 'user-data');
}
return path.resolve(args['user-data-dir'] || paths.getDefaultUserDataPath(process.platform));
}
// Set userData path before app 'ready' event and call to process.chdir
let userData = path.resolve(args['user-data-dir'] || paths.getDefaultUserDataPath(process.platform));
app.setPath('userData', userData);
app.setPath('userData', getUserDataPath());
// Update cwd based on environment and platform
try {
......
......@@ -323,6 +323,11 @@ function main() {
VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'],
VSCODE_LOGS: process.env['VSCODE_LOGS']
};
if (process.env['VSCODE_PORTABLE']) {
instanceEnv['VSCODE_PORTABLE'] = process.env['VSCODE_PORTABLE'];
}
assign(process.env, instanceEnv);
// Startup
......
......@@ -113,8 +113,8 @@ export class CursorUndo extends EditorAction {
constructor() {
super({
id: 'cursorUndo',
label: nls.localize('cursor.undo', "Remove Selection of Last Find Match"),
alias: 'Remove Selection of Last Find Match',
label: nls.localize('cursor.undo', "Soft Undo"),
alias: 'Soft Undo',
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
......
......@@ -90,7 +90,13 @@ export class EnvironmentService implements IEnvironmentService {
get userHome(): string { return os.homedir(); }
@memoize
get userDataPath(): string { return parseUserDataDir(this._args, process); }
get userDataPath(): string {
if (process.env['VSCODE_PORTABLE']) {
return path.join(process.env['VSCODE_PORTABLE'], 'user-data');
}
return parseUserDataDir(this._args, process);
}
get appNameLong(): string { return product.nameLong; }
......@@ -127,7 +133,19 @@ export class EnvironmentService implements IEnvironmentService {
get installSourcePath(): string { return path.join(this.userDataPath, 'installSource'); }
@memoize
get extensionsPath(): string { return parsePathArg(this._args['extensions-dir'], process) || process.env['VSCODE_EXTENSIONS'] || path.join(this.userHome, product.dataFolderName, 'extensions'); }
get extensionsPath(): string {
const fromArgs = parsePathArg(this._args['extensions-dir'], process);
if (fromArgs) {
return fromArgs;
} else if (process.env['VSCODE_EXTENSIONS']) {
return process.env['VSCODE_EXTENSIONS'];
} else if (process.env['VSCODE_PORTABLE']) {
return path.join(process.env['VSCODE_PORTABLE'], 'extensions');
} else {
return path.join(this.userHome, product.dataFolderName, 'extensions');
}
}
@memoize
get extensionDevelopmentPath(): string { return this._args.extensionDevelopmentPath ? path.normalize(this._args.extensionDevelopmentPath) : this._args.extensionDevelopmentPath; }
......
......@@ -13,6 +13,7 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
import { join } from 'vs/base/common/paths';
import { Limiter } from 'vs/base/common/async';
import { fromNodeEventEmitter, anyEvent, mapEvent, debounceEvent } from 'vs/base/common/event';
import { Schemas } from 'vs/base/common/network';
export class ExtensionsLifecycle extends Disposable {
......@@ -36,7 +37,7 @@ export class ExtensionsLifecycle extends Disposable {
}
private parseUninstallScript(extension: ILocalExtension): { uninstallHook: string, args: string[] } {
if (extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts']['vscode:uninstall'] === 'string') {
if (extension.location.scheme === Schemas.file && extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts']['vscode:uninstall'] === 'string') {
const uninstallScript = (<string>extension.manifest['scripts']['vscode:uninstall']).split(' ');
if (uninstallScript.length < 2 || uninstallScript[0] !== 'node' || !uninstallScript[1]) {
this.logService.warn(extension.identifier.id, 'Uninstall script should be a node script');
......
......@@ -658,7 +658,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
private preUninstallExtension(extension: ILocalExtension): TPromise<void> {
return pfs.exists(extension.location.path)
return pfs.exists(extension.location.fsPath)
.then(exists => exists ? null : TPromise.wrapError(new Error(nls.localize('notExists', "Could not find extension"))))
.then(() => {
this.logService.info('Uninstalling extension:', extension.identifier.id);
......@@ -788,7 +788,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
private removeExtension(extension: ILocalExtension, type: string): TPromise<void> {
this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id);
return pfs.rimraf(extension.location.path).then(() => this.logService.info('Deleted from disk', extension.identifier.id));
return pfs.rimraf(extension.location.fsPath).then(() => this.logService.info('Deleted from disk', extension.identifier.id));
}
private isUninstalled(id: string): TPromise<boolean> {
......
......@@ -17,6 +17,7 @@ import { isValidLocalization, ILocalizationsService, LanguageType } from 'vs/pla
import product from 'vs/platform/node/product';
import { distinct, equals } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { Schemas } from 'vs/base/common/network';
interface ILanguagePack {
hash: string;
......@@ -138,7 +139,7 @@ class LanguagePacksCache extends Disposable {
private createLanguagePacksFromExtension(languagePacks: { [language: string]: ILanguagePack }, extension: ILocalExtension): void {
const extensionIdentifier = { id: getGalleryExtensionIdFromLocal(extension), uuid: extension.identifier.uuid };
for (const localizationContribution of extension.manifest.contributes.localizations) {
if (isValidLocalization(localizationContribution)) {
if (extension.location.scheme === Schemas.file && isValidLocalization(localizationContribution)) {
let languagePack = languagePacks[localizationContribution.languageId];
if (!languagePack) {
languagePack = { hash: '', extensions: [], translations: {} };
......
......@@ -73,6 +73,7 @@ export interface IProductConfiguration {
'darwin': string;
};
logUploaderUrl: string;
portable?: string;
}
export interface ISurveyData {
......
......@@ -8,7 +8,6 @@
import * as nls from 'vs/nls';
import { readFile } from 'vs/base/node/pfs';
import * as semver from 'semver';
import * as path from 'path';
import { Event, Emitter } from 'vs/base/common/event';
import { index } from 'vs/base/common/arrays';
import { assign } from 'vs/base/common/objects';
......@@ -40,6 +39,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { groupBy } from 'vs/base/common/collections';
import { Schemas } from 'vs/base/common/network';
import { join } from 'vs/base/common/paths';
interface IExtensionStateProvider<T> {
(extension: Extension): T;
......@@ -138,7 +138,7 @@ class Extension implements IExtension {
private get localIconUrl(): string {
if (this.local && this.local.manifest.icon) {
return this.local.location.with({ path: path.join(this.local.location.path, this.local.manifest.icon) }).toString();
return this.local.location.with({ path: join(this.local.location.path, this.local.manifest.icon) }).toString();
}
return null;
}
......
......@@ -36,6 +36,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows';
import { URLService } from 'vs/platform/url/common/urlService';
import URI from 'vs/base/common/uri';
import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService';
import { Schemas } from 'vs/base/common/network';
suite('ExtensionsActions Test', () => {
......@@ -94,14 +95,14 @@ suite('ExtensionsActions Test', () => {
});
test('Install action is disabled when there is no extension', () => {
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
assert.ok(!testObject.enabled);
});
test('Test Install action when state is installed', () => {
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
return workbenchService.queryLocal()
......@@ -119,7 +120,7 @@ suite('ExtensionsActions Test', () => {
test('Test Install action when state is installing', () => {
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
return workbenchService.queryGallery()
......@@ -135,7 +136,7 @@ suite('ExtensionsActions Test', () => {
test('Test Install action when state is uninstalled', () => {
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
return workbenchService.queryGallery()
......@@ -147,7 +148,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test Install action when extension is system action', () => {
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a', {}, { type: LocalExtensionType.System });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -161,7 +162,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test Install action when extension doesnot has gallery', () => {
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -242,14 +243,14 @@ suite('ExtensionsActions Test', () => {
});
test('Test CombinedInstallAction when there is no extension', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
assert.ok(!testObject.enabled);
assert.equal('extension-action prominent install no-extension', testObject.class);
});
test('Test CombinedInstallAction when extension is system extension', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a', {}, { type: LocalExtensionType.System });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -263,7 +264,7 @@ suite('ExtensionsActions Test', () => {
test('Test CombinedInstallAction when installAction is enabled', () => {
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
......@@ -277,7 +278,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test CombinedInstallAction when unInstallAction is enabled', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -291,7 +292,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test CombinedInstallAction when state is installing', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
const gallery = aGalleryExtension('a');
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
......@@ -307,7 +308,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test CombinedInstallAction when state is uninstalling', () => {
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a');
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -322,13 +323,13 @@ suite('ExtensionsActions Test', () => {
});
test('Test UpdateAction when there is no extension', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
assert.ok(!testObject.enabled);
});
test('Test UpdateAction when extension is uninstalled', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const gallery = aGalleryExtension('a', { version: '1.0.0' });
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
return instantiationService.get(IExtensionsWorkbenchService).queryGallery()
......@@ -339,7 +340,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test UpdateAction when extension is installed and not outdated', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a', { version: '1.0.0' });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -353,7 +354,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test UpdateAction when extension is installed outdated and system extension', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a', { version: '1.0.0' }, { type: LocalExtensionType.System });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -367,7 +368,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test UpdateAction when extension is installed outdated and user extension', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a', { version: '1.0.0' });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......@@ -381,7 +382,7 @@ suite('ExtensionsActions Test', () => {
});
test('Test UpdateAction when extension is installing and outdated and user extension', () => {
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) });
const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction, <IExtensionManagementServer>{ extensionManagementService: null, location: URI.from({ scheme: Schemas.file }) });
const local = aLocalExtension('a', { version: '1.0.0' });
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
......
......@@ -8,7 +8,6 @@
import * as sinon from 'sinon';
import * as assert from 'assert';
import * as fs from 'fs';
import * as path from 'path';
import { assign } from 'vs/base/common/objects';
import { TPromise } from 'vs/base/common/winjs.base';
import { generateUuid } from 'vs/base/common/uuid';
......@@ -194,8 +193,8 @@ suite('ExtensionsWorkbenchService Test', () => {
assert.equal('1.1.0', actual.version);
assert.equal('1.1.0', actual.latestVersion);
assert.equal('localDescription1', actual.description);
assert.equal(expected1.location.with({ path: path.join(expected1.location.path, 'localIcon1') }).toString(), actual.iconUrl);
assert.equal(expected1.location.with({ path: path.join(expected1.location.path, 'localIcon1') }).toString(), actual.iconUrlFallback);
assert.equal('file:///localPath1/localIcon1', actual.iconUrl);
assert.equal('file:///localPath1/localIcon1', actual.iconUrlFallback);
assert.equal(null, actual.licenseUrl);
assert.equal(ExtensionState.Installed, actual.state);
assert.equal(null, actual.installCount);
......
......@@ -124,10 +124,10 @@
}
.settings-editor > .settings-body > .settings-tree-container .setting-item {
padding-top: 5px;
cursor: default;
white-space: normal;
height: 100%;
min-height: 75px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.odd:not(.focused):not(.selected):not(:hover),
......@@ -148,7 +148,6 @@
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-title {
margin-top: 2px;
line-height: initial;
}
......@@ -176,18 +175,19 @@
opacity: 0.7;
}
.settings-editor > .settings-body > .settings-tree-container .setting-measure-container.monaco-tree-row {
padding-left: 15px;
opacity: 0;
max-width: 800px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description {
opacity: 0.7;
margin-top: 3px;
overflow: hidden;
white-space: nowrap;
white-space: pre;
text-overflow: ellipsis;
}
.settings-editor > .settings-body > .settings-tree-container .setting-measure-container.monaco-tree-row {
padding-left: 15px;
opacity: 0;
max-width: 800px;
height: 18px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-description,
......@@ -196,15 +196,24 @@
white-space: pre-wrap;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > *:first-child {
min-width: 250px;
display: inline-block;
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value {
margin-top: 5px;
margin-bottom: 9px; /* Needed when measuring an expanded row */
display: flex;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value {
margin-top: 10px;
margin-bottom: 8px;
display: inline-block;
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-number {
min-width: 200px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-enum,
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-string {
flex: 1;
min-width: initial;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .setting-item-control.setting-type-enum > *:first-child {
width: 100%;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value > .edit-in-settings-button,
......@@ -231,7 +240,7 @@
visibility: hidden;
width: initial;
padding-top: 0px; /* So focus outline doesn't overlap the control above */
padding-top: 2px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.is-configured .setting-item-value > .setting-reset-button.monaco-button {
......
......@@ -54,13 +54,13 @@ export class SettingsEditor2 extends BaseEditor {
private settingsTree: WorkbenchTree;
private treeDataSource: SettingsDataSource;
private delayedModifyLogging: Delayer<void>;
private delayedFilterLogging: Delayer<void>;
private localSearchDelayer: Delayer<void>;
private remoteSearchThrottle: ThrottledDelayer<void>;
private searchInProgress: TPromise<void>;
private pendingSettingModifiedReport: { key: string, value: any };
private settingUpdateDelayer: Delayer<void>;
private pendingSettingUpdate: { key: string, value: any };
private selectedElement: TreeElement;
......@@ -81,12 +81,13 @@ export class SettingsEditor2 extends BaseEditor {
@IContextKeyService contextKeyService: IContextKeyService
) {
super(SettingsEditor2.ID, telemetryService, themeService);
this.delayedModifyLogging = new Delayer<void>(1000);
this.delayedFilterLogging = new Delayer<void>(1000);
this.localSearchDelayer = new Delayer(100);
this.remoteSearchThrottle = new ThrottledDelayer(200);
this.viewState = { settingsTarget: ConfigurationTarget.USER };
this.settingUpdateDelayer = new Delayer<void>(500);
this.inSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(contextKeyService);
this.searchFocusContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(contextKeyService);
......@@ -320,31 +321,36 @@ export class SettingsEditor2 extends BaseEditor {
}
private onDidChangeSetting(key: string, value: any): void {
// ConfigurationService displays the error if this fails.
// Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change
this.configurationService.updateValue(key, value, <ConfigurationTarget>this.settingsTargetsWidget.settingsTarget)
.then(() => this.refreshTreeAndMaintainFocus());
const reportModifiedProps = {
key,
query: this.searchWidget.getValue(),
searchResults: this.searchResultModel && this.searchResultModel.getUniqueResults(),
rawResults: this.searchResultModel && this.searchResultModel.getRawResults(),
showConfiguredOnly: this.viewState.showConfiguredOnly,
isReset: typeof value === 'undefined',
settingsTarget: this.settingsTargetsWidget.settingsTarget as SettingsTarget
};
if (this.pendingSettingModifiedReport && key !== this.pendingSettingModifiedReport.key) {
this.reportModifiedSetting(reportModifiedProps);
if (this.pendingSettingUpdate && this.pendingSettingUpdate.key !== key) {
this.updateChangedSetting(key, value);
}
this.pendingSettingModifiedReport = { key, value };
this.delayedModifyLogging.trigger(() => this.reportModifiedSetting(reportModifiedProps));
this.pendingSettingUpdate = { key, value };
this.settingUpdateDelayer.trigger(() => this.updateChangedSetting(key, value));
}
private updateChangedSetting(key: string, value: any): TPromise<void> {
// ConfigurationService displays the error if this fails.
// Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change
return this.configurationService.updateValue(key, value, <ConfigurationTarget>this.settingsTargetsWidget.settingsTarget)
.then(() => this.refreshTreeAndMaintainFocus())
.then(() => {
const reportModifiedProps = {
key,
query: this.searchWidget.getValue(),
searchResults: this.searchResultModel && this.searchResultModel.getUniqueResults(),
rawResults: this.searchResultModel && this.searchResultModel.getRawResults(),
showConfiguredOnly: this.viewState.showConfiguredOnly,
isReset: typeof value === 'undefined',
settingsTarget: this.settingsTargetsWidget.settingsTarget as SettingsTarget
};
return this.reportModifiedSetting(reportModifiedProps);
});
}
private reportModifiedSetting(props: { key: string, query: string, searchResults: ISearchResult[], rawResults: ISearchResult[], showConfiguredOnly: boolean, isReset: boolean, settingsTarget: SettingsTarget }): void {
this.pendingSettingModifiedReport = null;
this.pendingSettingUpdate = null;
const remoteResult = props.searchResults && props.searchResults[SearchResultIdx.Remote];
const localResult = props.searchResults && props.searchResults[SearchResultIdx.Local];
......
......@@ -298,7 +298,7 @@ export class SettingsRenderer implements IRenderer {
this.renderSettingElement(tree, element, template);
const height = measureHelper.offsetHeight;
this.measureContainer.removeChild(measureHelper);
this.measureContainer.removeChild(this.measureContainer.firstChild);
return Math.max(height, SettingsRenderer.SETTING_ROW_HEIGHT);
}
......@@ -423,11 +423,11 @@ export class SettingsRenderer implements IRenderer {
resetButton.element.classList.add('setting-reset-button');
resetButton.element.tabIndex = isSelected ? 0 : -1;
attachButtonStyler(resetButton, this.themeService, {
template.toDispose.push(attachButtonStyler(resetButton, this.themeService, {
buttonBackground: Color.transparent.toString(),
buttonHoverBackground: Color.transparent.toString(),
buttonForeground: editorActiveLinkForeground
});
}));
template.toDispose.push(resetButton.onDidClick(e => {
this._onDidChangeSetting.fire({ key: element.setting.key, value: undefined });
......@@ -448,56 +448,61 @@ export class SettingsRenderer implements IRenderer {
private renderValue(element: ISettingElement, isSelected: boolean, template: ISettingItemTemplate): void {
const onChange = value => this._onDidChangeSetting.fire({ key: element.setting.key, value });
template.valueElement.innerHTML = '';
const valueControlElement = DOM.append(template.valueElement, $('.setting-item-control'));
if (element.enum && (element.valueType === 'string' || !element.valueType)) {
this.renderEnum(element, isSelected, template, onChange);
valueControlElement.classList.add('setting-type-enum');
this.renderEnum(element, isSelected, template, valueControlElement, onChange);
} else if (element.valueType === 'boolean') {
this.renderBool(element, isSelected, template, onChange);
valueControlElement.classList.add('setting-type-boolean');
this.renderBool(element, isSelected, template, valueControlElement, onChange);
} else if (element.valueType === 'string') {
this.renderText(element, isSelected, template, onChange);
valueControlElement.classList.add('setting-type-string');
this.renderText(element, isSelected, template, valueControlElement, onChange);
} else if (element.valueType === 'number' || element.valueType === 'integer') {
this.renderText(element, isSelected, template, value => onChange(parseInt(value)));
valueControlElement.classList.add('setting-type-number');
this.renderText(element, isSelected, template, valueControlElement, value => onChange(parseInt(value)));
} else {
this.renderEditInSettingsJson(element, isSelected, template);
valueControlElement.classList.add('setting-type-complex');
this.renderEditInSettingsJson(element, isSelected, template, valueControlElement);
}
}
private renderBool(element: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, onChange: (value: boolean) => void): void {
const checkboxElement = <HTMLInputElement>DOM.append(template.valueElement, $('input.setting-value-checkbox.setting-value-input'));
private renderBool(dataElement: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: boolean) => void): void {
const checkboxElement = <HTMLInputElement>DOM.append(element, $('input.setting-value-checkbox.setting-value-input'));
checkboxElement.type = 'checkbox';
checkboxElement.checked = element.value;
checkboxElement.checked = dataElement.value;
checkboxElement.tabIndex = isSelected ? 0 : -1;
template.toDispose.push(DOM.addDisposableListener(checkboxElement, 'change', e => onChange(checkboxElement.checked)));
}
private renderEnum(element: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, onChange: (value: string) => void): void {
const idx = element.enum.indexOf(element.value);
const displayOptions = element.enum.map(escapeInvisibleChars);
private renderEnum(dataElement: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: string) => void): void {
const idx = dataElement.enum.indexOf(dataElement.value);
const displayOptions = dataElement.enum.map(escapeInvisibleChars);
const selectBox = new SelectBox(displayOptions, idx, this.contextViewService);
template.toDispose.push(selectBox);
template.toDispose.push(attachSelectBoxStyler(selectBox, this.themeService));
selectBox.render(template.valueElement);
if (template.valueElement.firstElementChild) {
template.valueElement.firstElementChild.setAttribute('tabindex', isSelected ? '0' : '-1');
selectBox.render(element);
if (element.firstElementChild) {
element.firstElementChild.setAttribute('tabindex', isSelected ? '0' : '-1');
}
template.toDispose.push(
selectBox.onDidSelect(e => onChange(element.enum[e.index])));
selectBox.onDidSelect(e => onChange(dataElement.enum[e.index])));
}
private renderText(element: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, onChange: (value: string) => void): void {
const inputBox = new InputBox(template.valueElement, this.contextViewService);
private renderText(dataElement: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement, onChange: (value: string) => void): void {
const inputBox = new InputBox(element, this.contextViewService);
template.toDispose.push(attachInputBoxStyler(inputBox, this.themeService));
template.toDispose.push(inputBox);
inputBox.value = element.value;
inputBox.value = dataElement.value;
inputBox.inputElement.tabIndex = isSelected ? 0 : -1;
template.toDispose.push(
inputBox.onDidChange(e => onChange(e)));
}
private renderEditInSettingsJson(element: ISettingElement, isSelected: boolean, template: ISettingItemTemplate): void {
const openSettingsButton = new Button(template.valueElement, { title: true, buttonBackground: null, buttonHoverBackground: null });
private renderEditInSettingsJson(dataElement: ISettingElement, isSelected: boolean, template: ISettingItemTemplate, element: HTMLElement): void {
const openSettingsButton = new Button(element, { title: true, buttonBackground: null, buttonHoverBackground: null });
openSettingsButton.onDidClick(() => this._onDidOpenSettings.fire());
openSettingsButton.label = localize('editInSettingsJson', "Edit in settings.json");
openSettingsButton.element.classList.add('edit-in-settings-button');
......
......@@ -116,7 +116,8 @@ function cleanEnv() {
'PTYCOLS',
'PTYROWS',
'PTYSHELLCMDLINE',
'VSCODE_LOGS'
'VSCODE_LOGS',
'VSCODE_PORTABLE'
];
keys.forEach(function (key) {
if (process.env[key]) {
......
......@@ -16,7 +16,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
constructor(
localExtensionManagementService: IExtensionManagementService
) {
this.extensionManagementServers = [{ extensionManagementService: localExtensionManagementService, location: URI.from({ scheme: 'vscode-local', authority: 'local' }) }];
this.extensionManagementServers = [{ extensionManagementService: localExtensionManagementService, location: URI.from({ scheme: Schemas.file }) }];
}
getExtensionManagementServer(location: URI): IExtensionManagementServer {
......@@ -37,7 +37,7 @@ export class SingleServerExtensionManagementServerService implements IExtensionM
}
getExtensionManagementServer(location: URI): IExtensionManagementServer {
location = location.scheme === Schemas.file ? URI.from({ scheme: 'vscode-local', authority: 'local' }) : location;
location = location.scheme === Schemas.file ? URI.from({ scheme: Schemas.file }) : location;
return this.extensionManagementServers.filter(server => location.authority === server.location.authority)[0];
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册