提交 0575dd40 编写于 作者: B Benjamin Pasero

improve static argv configuration (#15211)

上级 193fead4
......@@ -72,8 +72,8 @@
"url": "vscode://schemas/workspaceConfig"
},
{
"fileMatch": "%APP_SETTINGS_HOME%/locale.json",
"url": "vscode://schemas/locale"
"fileMatch": "**/argv.json",
"url": "vscode://schemas/argv"
},
{
"fileMatch": "/.vscode/settings.json",
......
......@@ -38,6 +38,12 @@ setCurrentWorkingDirectory();
// Global app listeners
registerListeners();
// Cached data
const nodeCachedDataDir = getNodeCachedDir();
// Configure static command line arguments
const argvConfig = configureCommandlineSwitchesSync(args);
/**
* Support user defined locale: load it early before app('ready')
* to have more things running in parallel.
......@@ -45,20 +51,12 @@ registerListeners();
* @type {Promise<import('./vs/base/node/languagePacks').NLSConfiguration>} nlsConfig | undefined
*/
let nlsConfigurationPromise = undefined;
const userDefinedLocale = getUserDefinedLocale();
const metaDataFile = path.join(__dirname, 'nls.metadata.json');
userDefinedLocale.then(locale => {
if (locale && !nlsConfigurationPromise) {
nlsConfigurationPromise = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
}
});
// Cached data
const nodeCachedDataDir = getNodeCachedDir();
// Configure command line switches
configureCommandlineSwitches(args);
const metaDataFile = path.join(__dirname, 'nls.metadata.json');
const locale = getUserDefinedLocale(argvConfig);
if (locale) {
nlsConfigurationPromise = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
}
// Load our code once ready
app.once('ready', function () {
......@@ -100,9 +98,9 @@ async function onReady() {
perf.mark('main:appReady');
try {
const [cachedDataDir, locale] = await Promise.all([nodeCachedDataDir.ensureExists(), userDefinedLocale]);
const [cachedDataDir, nlsConfig] = await Promise.all([nodeCachedDataDir.ensureExists(), resolveNlsConfiguration()]);
startup(cachedDataDir, await resolveNlsConfiguration(locale));
startup(cachedDataDir, nlsConfig);
} catch (error) {
console.error(error);
}
......@@ -113,22 +111,34 @@ async function onReady() {
*
* @param {ParsedArgs} cliArgs
*/
function configureCommandlineSwitches(cliArgs) {
function configureCommandlineSwitchesSync(cliArgs) {
const SUPPORTED_ELECTRON_SWITCHES = [
// alias from us for --disable-gpu
'disable-hardware-acceleration',
// provided by Electron
'disable-color-correct-rendering'
];
// Read argv config
const argvConfig = readArgvConfig();
const argvConfig = readArgvConfigSync();
// Append each flag to Electron
Object.keys(argvConfig).forEach(flag => {
const value = argvConfig[flag];
if (value === true || value === 'true') {
if (flag === 'disable-gpu') {
Object.keys(argvConfig).forEach(argvKey => {
if (SUPPORTED_ELECTRON_SWITCHES.indexOf(argvKey) === -1) {
return; // unsupported argv key
}
const argvValue = argvConfig[argvKey];
if (argvValue === true || argvValue === 'true') {
if (argvKey === 'disable-hardware-acceleration') {
app.disableHardwareAcceleration(); // needs to be called explicitly
} else {
app.commandLine.appendArgument(argvKey);
}
app.commandLine.appendArgument(flag);
} else {
app.commandLine.appendSwitch(flag, value);
app.commandLine.appendSwitch(argvKey, argvValue);
}
});
......@@ -137,9 +147,11 @@ function configureCommandlineSwitches(cliArgs) {
if (jsFlags) {
app.commandLine.appendSwitch('js-flags', jsFlags);
}
return argvConfig;
}
function readArgvConfig() {
function readArgvConfigSync() {
// Read or create the argv.json config file sync before app('ready')
const argvConfigPath = getArgvConfigPath();
......@@ -148,35 +160,7 @@ function readArgvConfig() {
argvConfig = JSON.parse(stripComments(fs.readFileSync(argvConfigPath).toString()));
} catch (error) {
if (error && error.code === 'ENOENT') {
try {
const argvConfigPathDirname = path.dirname(argvConfigPath);
if (!fs.existsSync(argvConfigPathDirname)) {
fs.mkdirSync(argvConfigPathDirname);
}
// Create initial argv.json if not existing
fs.writeFileSync(argvConfigPath, `// This configuration file allows to pass permanent command line arguments to VSCode.
//
// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT
//
// If the command line argument does not have any values, simply assign
// it in the JSON below with a value of 'true'. Otherwise, put the value
// directly.
//
// If you see rendering issues in VSCode and have a better experience
// with software rendering, you can configure this by adding:
//
// 'disable-gpu': true
//
// NOTE: Changing this file requires a restart of VSCode.
{
// Enabled by default by VSCode to resolve color issues in the renderer
// See https://github.com/Microsoft/vscode/issues/51791 for details
"disable-color-correct-rendering": true
}`);
} catch (error) {
console.error(`Unable to create argv.json configuration file in ${argvConfigPath}, falling back to defaults (${error})`);
}
createDefaultArgvConfigSync(argvConfigPath);
} else {
console.warn(`Unable to read argv.json configuration file in ${argvConfigPath}, falling back to defaults (${error})`);
}
......@@ -192,6 +176,70 @@ function readArgvConfig() {
return argvConfig;
}
/**
* @param {string} argvConfigPath
*/
function createDefaultArgvConfigSync(argvConfigPath) {
try {
// Ensure argv config parent exists
const argvConfigPathDirname = path.dirname(argvConfigPath);
if (!fs.existsSync(argvConfigPathDirname)) {
fs.mkdirSync(argvConfigPathDirname);
}
// Migrate over legacy locale
const localeConfigPath = path.join(userDataPath, 'User', 'locale.json');
const legacyLocale = getLegacyUserDefinedLocaleSync(localeConfigPath);
if (legacyLocale) {
try {
fs.unlinkSync(localeConfigPath);
} catch (error) {
//ignore
}
}
// Default argv content
const defaultArgvConfigContent = [
'// This configuration file allows to pass permanent command line arguments to VSCode.',
'// Only a subset of arguments is currently supported to reduce the likelyhood of breaking',
'// the installation.',
'//',
'// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT',
'//',
'// If the command line argument does not have any values, simply assign',
'// it in the JSON below with a value of \'true\'. Otherwise, put the value',
'// directly.',
'//',
'// If you see rendering issues in VSCode and have a better experience',
'// with software rendering, you can configure this by adding:',
'//',
'// \'disable-hardware-acceleration\': true',
'//',
'// NOTE: Changing this file requires a restart of VSCode.',
'{',
' // Enabled by default by VSCode to resolve color issues in the renderer',
' // See https://github.com/Microsoft/vscode/issues/51791 for details',
' "disable-color-correct-rendering": true'
];
if (legacyLocale) {
defaultArgvConfigContent[defaultArgvConfigContent.length - 1] = `${defaultArgvConfigContent[defaultArgvConfigContent.length - 1]},`; // append trailing ","
defaultArgvConfigContent.push('');
defaultArgvConfigContent.push(' // Display language of VSCode');
defaultArgvConfigContent.push(` "locale": "${legacyLocale}"`);
}
defaultArgvConfigContent.push('}');
// Create initial argv.json with default content
fs.writeFileSync(argvConfigPath, defaultArgvConfigContent.join('\n'));
} catch (error) {
console.error(`Unable to create argv.json configuration file in ${argvConfigPath}, falling back to defaults (${error})`);
}
}
function getArgvConfigPath() {
const vscodePortable = process.env['VSCODE_PORTABLE'];
if (vscodePortable) {
......@@ -350,22 +398,13 @@ function getNodeCachedDir() {
/**
* Resolve the NLS configuration
*
* @param {string | undefined} locale
* @return {Promise<import('./vs/base/node/languagePacks').NLSConfiguration>}
*/
async function resolveNlsConfiguration(locale) {
// First, we need to test a user defined locale. If it fails we try the app locale.
// If that fails we fall back to English.
if (locale && !nlsConfigurationPromise) {
nlsConfigurationPromise = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
} else if (!nlsConfigurationPromise) {
nlsConfigurationPromise = Promise.resolve(undefined);
}
async function resolveNlsConfiguration() {
// First, we need to test a user defined locale. If it fails we try the app locale.
// If that fails we fall back to English.
let nlsConfiguration = await nlsConfigurationPromise;
let nlsConfiguration = nlsConfigurationPromise ? await nlsConfigurationPromise : undefined;
if (!nlsConfiguration) {
// Try to use the app locale. Please note that the app locale is only
......@@ -425,18 +464,25 @@ function stripComments(content) {
* the language bundles have lower case language tags and we always lower case
* the locale we receive from the user or OS.
*
* @returns {Promise<string>}
* @param {{ locale: string | undefined; }} argvConfig
* @returns {string | undefined}
*/
async function getUserDefinedLocale() {
function getUserDefinedLocale(argvConfig) {
const locale = args['locale'];
if (locale) {
return locale.toLowerCase();
return locale.toLowerCase(); // a directly provided --locale always wins
}
const localeConfig = path.join(userDataPath, 'User', 'locale.json');
return argvConfig.locale && typeof argvConfig.locale === 'string' ? argvConfig.locale.toLowerCase() : undefined;
}
/**
* @param {string} localeConfigPath
* @returns {string | undefined}
*/
function getLegacyUserDefinedLocaleSync(localeConfigPath) {
try {
const content = stripComments(await bootstrap.readFile(localeConfig));
const content = stripComments(fs.readFileSync(localeConfigPath).toString());
const value = JSON.parse(content).locale;
return value && typeof value === 'string' ? value.toLowerCase() : undefined;
......
......@@ -115,7 +115,6 @@ export interface IEnvironmentService {
settingsResource: URI;
keybindingsResource: URI;
keyboardLayoutResource: URI;
localeResource: URI;
argvResource: URI;
// sync resources
......
......@@ -303,7 +303,6 @@ export function buildVersionMessage(version: string | undefined, commit: string
return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`;
}
export function addArg(argv: string[], ...args: string[]): string[] {
const endOfArgsMarkerIndex = argv.indexOf('--');
if (endOfArgsMarkerIndex === -1) {
......
......@@ -139,9 +139,6 @@ export class EnvironmentService implements IEnvironmentService {
@memoize
get keyboardLayoutResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); }
@memoize
get localeResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'locale.json'); }
@memoize
get argvResource(): URI {
const vscodePortable = process.env['VSCODE_PORTABLE'];
......
......@@ -6,13 +6,11 @@
import { localize } from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Disposable } from 'vs/base/common/lifecycle';
import { ConfigureLocaleAction } from 'vs/workbench/contrib/localizations/browser/localizationsActions';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import { IExtensionManagementService, DidInstallExtensionEvent, IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
......@@ -35,7 +33,6 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLocaleAction,
export class LocalizationWorkbenchContribution extends Disposable implements IWorkbenchContribution {
constructor(
@ILocalizationsService private readonly localizationService: ILocalizationsService,
@INotificationService private readonly notificationService: INotificationService,
@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
......@@ -47,26 +44,10 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
@ITelemetryService private readonly telemetryService: ITelemetryService
) {
super();
this.updateLocaleDefintionSchema();
this.checkAndInstall();
this._register(this.localizationService.onDidLanguagesChange(() => this.updateLocaleDefintionSchema()));
this._register(this.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e)));
}
private updateLocaleDefintionSchema(): void {
this.localizationService.getLanguageIds()
.then(languageIds => {
let lowercaseLanguageIds: string[] = [];
languageIds.forEach((languageId) => {
let lowercaseLanguageId = languageId.toLowerCase();
if (lowercaseLanguageId !== languageId) {
lowercaseLanguageIds.push(lowercaseLanguageId);
}
});
registerLocaleDefinitionSchema([...languageIds, ...lowercaseLanguageIds]);
});
}
private onDidInstallExtension(e: DidInstallExtensionEvent): void {
if (e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) {
const locale = e.local.manifest.contributes.localizations[0].languageId;
......@@ -79,7 +60,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
[{
label: updateAndRestart ? localize('yes', "Yes") : localize('restart now', "Restart Now"),
run: () => {
const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.localeResource, [{ key: 'locale', value: locale }], true) : Promise.resolve(undefined);
const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.argvResource, [{ key: 'locale', value: locale }], true) : Promise.resolve(undefined);
updatePromise.then(() => this.hostService.restart(), e => this.notificationService.error(e));
}
}],
......@@ -225,31 +206,6 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
}
}
function registerLocaleDefinitionSchema(languages: string[]): void {
const localeDefinitionFileSchemaId = 'vscode://schemas/locale';
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
// Keep en-US since we generated files with that content.
jsonRegistry.registerSchema(localeDefinitionFileSchemaId, {
id: localeDefinitionFileSchemaId,
allowComments: true,
allowTrailingCommas: true,
description: 'Locale Definition file',
type: 'object',
default: {
'locale': 'en'
},
required: ['locale'],
properties: {
locale: {
type: 'string',
enum: languages,
description: localize('JsonSchema.locale', 'The UI Language to use.')
}
}
});
}
registerLocaleDefinitionSchema(platform.language ? [platform.language] : []);
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(LocalizationWorkbenchContribution, LifecyclePhase.Eventually);
......
......@@ -65,7 +65,7 @@ export class ConfigureLocaleAction extends Action {
}
if (selectedLanguage) {
await this.jsonEditingService.write(this.environmentService.localeResource, [{ key: 'locale', value: selectedLanguage.label }], true);
await this.jsonEditingService.write(this.environmentService.argvResource, [{ key: 'locale', value: selectedLanguage.label }], true);
const restart = await this.dialogService.confirm({
type: 'info',
message: localize('relaunchDisplayLanguageMessage', "A restart is required for the change in display language to take effect."),
......
......@@ -21,6 +21,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext } from 'vs/workbench/browser/contextkeys';
import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
// Actions
(function registerActions(): void {
......@@ -342,3 +343,32 @@ import { IElectronService } from 'vs/platform/electron/node/electron';
}
});
})();
// JSON Schemas
(function registerJSONSchemas(): void {
const argvDefinitionFileSchemaId = 'vscode://schemas/argv';
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(argvDefinitionFileSchemaId, {
id: argvDefinitionFileSchemaId,
allowComments: true,
allowTrailingCommas: true,
description: 'VSCode static command line definition file',
type: 'object',
additionalProperties: false,
properties: {
locale: {
type: 'string',
description: nls.localize('argv.locale', 'The display Language to use. Picking a different language requires the associated language pack to be installed.')
},
'disable-hardware-acceleration': {
type: 'boolean',
description: nls.localize('argv.disableHardwareAcceleration', 'Disables hardware acceleration. ONLY change this option if you encounter graphic issues.')
},
'disable-color-correct-rendering': {
type: 'boolean',
description: nls.localize('argv.disableColorCorrectRendering', 'Resolves issues around color profile selection. ONLY change this option if you encounter graphic issues.')
}
}
});
})();
......@@ -88,7 +88,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
this.userDataSyncLogResource = joinPath(options.logsPath, 'userDataSync.log');
this.keybindingsResource = joinPath(this.userRoamingDataHome, 'keybindings.json');
this.keyboardLayoutResource = joinPath(this.userRoamingDataHome, 'keyboardLayout.json');
this.localeResource = joinPath(this.userRoamingDataHome, 'locale.json');
this.argvResource = joinPath(this.userRoamingDataHome, 'argv.json');
this.backupHome = joinPath(this.userRoamingDataHome, BACKUPS);
this.configuration.backupWorkspaceResource = joinPath(this.backupHome, options.workspaceId);
......@@ -146,7 +145,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
settingsResource: URI;
keybindingsResource: URI;
keyboardLayoutResource: URI;
localeResource: URI;
argvResource: URI;
settingsSyncPreviewResource: URI;
userDataSyncLogResource: URI;
......
......@@ -783,11 +783,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return 'keybindings';
}
// Check for locale file
if (isEqual(this.resource, joinPath(this.environmentService.userRoamingDataHome, 'locale.json'))) {
return 'locale';
}
// Check for snippets
if (isEqualOrParent(this.resource, joinPath(this.environmentService.userRoamingDataHome, 'snippets'))) {
return 'snippets';
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册