mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-30 16:50:30 -04:00
Merge from vscode 05fc61ffb1aee9fd19173c32113daed079f9b7bd (#5074)
* Merge from vscode 05fc61ffb1aee9fd19173c32113daed079f9b7bd * fix tests
This commit is contained in:
@@ -97,7 +97,7 @@ export class LanguageConfigurationFileHandler {
|
||||
}
|
||||
|
||||
private _handleConfigFile(languageIdentifier: LanguageIdentifier, configFileLocation: URI): void {
|
||||
this._fileService.resolveContent(configFileLocation, { encoding: 'utf8' }).then((contents) => {
|
||||
this._fileService.readFile(configFileLocation).then((contents) => {
|
||||
const errors: ParseError[] = [];
|
||||
const configuration = <ILanguageConfiguration>parse(contents.value.toString(), errors);
|
||||
if (errors.length) {
|
||||
|
||||
@@ -559,7 +559,7 @@ class Launch extends AbstractLaunch implements ILaunch {
|
||||
const resource = this.uri;
|
||||
let created = false;
|
||||
|
||||
return this.fileService.resolveContent(resource).then(content => content.value, err => {
|
||||
return this.fileService.readFile(resource).then(content => content.value, err => {
|
||||
// launch.json not found: create one by collecting launch configs from debugConfigProviders
|
||||
return this.configurationManager.guessDebugger(type).then(adapter => {
|
||||
if (adapter) {
|
||||
@@ -576,19 +576,17 @@ class Launch extends AbstractLaunch implements ILaunch {
|
||||
}
|
||||
|
||||
created = true; // pin only if config file is created #8727
|
||||
return this.textFileService.write(resource, content).then(() => {
|
||||
// convert string into IContent; see #32135
|
||||
return content;
|
||||
});
|
||||
return this.textFileService.write(resource, content).then(() => content);
|
||||
});
|
||||
}).then(content => {
|
||||
if (!content) {
|
||||
return { editor: null, created: false };
|
||||
}
|
||||
const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
|
||||
const contentValue = content.toString();
|
||||
const index = contentValue.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
|
||||
let startLineNumber = 1;
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (content.charAt(i) === '\n') {
|
||||
if (contentValue.charAt(i) === '\n') {
|
||||
startLineNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,35 +29,37 @@ export function getTerminalLauncher() {
|
||||
}
|
||||
|
||||
let _DEFAULT_TERMINAL_LINUX_READY: Promise<string> | null = null;
|
||||
|
||||
export function getDefaultTerminalLinuxReady(): Promise<string> {
|
||||
if (!_DEFAULT_TERMINAL_LINUX_READY) {
|
||||
_DEFAULT_TERMINAL_LINUX_READY = new Promise<string>(c => {
|
||||
_DEFAULT_TERMINAL_LINUX_READY = new Promise<string>(resolve => {
|
||||
if (env.isLinux) {
|
||||
Promise.all<any>([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => {
|
||||
if (isDebian) {
|
||||
c('x-terminal-emulator');
|
||||
resolve('x-terminal-emulator');
|
||||
} else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') {
|
||||
c('gnome-terminal');
|
||||
resolve('gnome-terminal');
|
||||
} else if (process.env.DESKTOP_SESSION === 'kde-plasma') {
|
||||
c('konsole');
|
||||
resolve('konsole');
|
||||
} else if (process.env.COLORTERM) {
|
||||
c(process.env.COLORTERM);
|
||||
resolve(process.env.COLORTERM);
|
||||
} else if (process.env.TERM) {
|
||||
c(process.env.TERM);
|
||||
resolve(process.env.TERM);
|
||||
} else {
|
||||
c('xterm');
|
||||
resolve('xterm');
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
c('xterm');
|
||||
resolve('xterm');
|
||||
});
|
||||
}
|
||||
return _DEFAULT_TERMINAL_LINUX_READY;
|
||||
}
|
||||
|
||||
let _DEFAULT_TERMINAL_WINDOWS: string | null = null;
|
||||
|
||||
export function getDefaultTerminalWindows(): string {
|
||||
if (!_DEFAULT_TERMINAL_WINDOWS) {
|
||||
const isWoW64 = !!process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
|
||||
@@ -82,7 +84,7 @@ class WinTerminalService extends TerminalLauncher {
|
||||
|
||||
const exec = configuration.external.windowsExec || getDefaultTerminalWindows();
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
const title = `"${dir} - ${TERMINAL_TITLE}"`;
|
||||
const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code
|
||||
@@ -104,9 +106,11 @@ class WinTerminalService extends TerminalLauncher {
|
||||
};
|
||||
|
||||
const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options);
|
||||
cmd.on('error', e);
|
||||
cmd.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
|
||||
c(undefined);
|
||||
resolve(undefined);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -120,7 +124,7 @@ class MacTerminalService extends TerminalLauncher {
|
||||
|
||||
const terminalApp = configuration.external.osxExec || MacTerminalService.DEFAULT_TERMINAL_OSX;
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
if (terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') {
|
||||
|
||||
@@ -156,24 +160,26 @@ class MacTerminalService extends TerminalLauncher {
|
||||
|
||||
let stderr = '';
|
||||
const osa = cp.spawn(MacTerminalService.OSASCRIPT, osaArgs);
|
||||
osa.on('error', e);
|
||||
osa.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
osa.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
osa.on('exit', (code: number) => {
|
||||
if (code === 0) { // OK
|
||||
c(undefined);
|
||||
resolve(undefined);
|
||||
} else {
|
||||
if (stderr) {
|
||||
const lines = stderr.split('\n', 1);
|
||||
e(new Error(lines[0]));
|
||||
reject(new Error(lines[0]));
|
||||
} else {
|
||||
e(new Error(nls.localize('mac.terminal.script.failed', "Script '{0}' failed with exit code {1}", script, code)));
|
||||
reject(new Error(nls.localize('mac.terminal.script.failed', "Script '{0}' failed with exit code {1}", script, code)));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
e(new Error(nls.localize('mac.terminal.type.not.supported', "'{0}' not supported", terminalApp)));
|
||||
reject(new Error(nls.localize('mac.terminal.type.not.supported', "'{0}' not supported", terminalApp)));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -188,7 +194,7 @@ class LinuxTerminalService extends TerminalLauncher {
|
||||
const terminalConfig = configuration.external;
|
||||
const execThenable: Promise<string> = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady();
|
||||
|
||||
return new Promise<number | undefined>((c, e) => {
|
||||
return new Promise<number | undefined>((resolve, reject) => {
|
||||
|
||||
let termArgs: string[] = [];
|
||||
//termArgs.push('--title');
|
||||
@@ -218,19 +224,21 @@ class LinuxTerminalService extends TerminalLauncher {
|
||||
|
||||
let stderr = '';
|
||||
const cmd = cp.spawn(exec, termArgs, options);
|
||||
cmd.on('error', e);
|
||||
cmd.on('error', err => {
|
||||
reject(improveError(err));
|
||||
});
|
||||
cmd.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
cmd.on('exit', (code: number) => {
|
||||
if (code === 0) { // OK
|
||||
c(undefined);
|
||||
resolve(undefined);
|
||||
} else {
|
||||
if (stderr) {
|
||||
const lines = stderr.split('\n', 1);
|
||||
e(new Error(lines[0]));
|
||||
reject(new Error(lines[0]));
|
||||
} else {
|
||||
e(new Error(nls.localize('linux.term.failed', "'{0}' failed with exit code {1}", exec, code)));
|
||||
reject(new Error(nls.localize('linux.term.failed', "'{0}' failed with exit code {1}", exec, code)));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -239,6 +247,16 @@ class LinuxTerminalService extends TerminalLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to turn OS errors into more meaningful error messages
|
||||
*/
|
||||
function improveError(err: Error): Error {
|
||||
if (err['errno'] === 'ENOENT' && err['path']) {
|
||||
return new Error(nls.localize('ext.term.app.not.found', "can't find terminal application '{0}'", err['path']));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote args if necessary and combine into a space separated string.
|
||||
*/
|
||||
|
||||
@@ -46,6 +46,7 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { extname } from 'vs/base/common/resources';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
const milliSecondsInADay = 1000 * 60 * 60 * 24;
|
||||
const choiceNever = localize('neverShowAgain', "Don't Show Again");
|
||||
@@ -111,6 +112,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||
@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExperimentService private readonly experimentService: IExperimentService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -339,8 +341,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return Promise.resolve(this.fileService.resolveContent(workspace.configuration)
|
||||
.then(content => <IExtensionsConfigContent>(json.parse(content.value)['extensions']), err => null));
|
||||
return Promise.resolve(this.fileService.readFile(workspace.configuration)
|
||||
.then(content => <IExtensionsConfigContent>(json.parse(content.value.toString())['extensions']), err => null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,8 +352,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
const extensionsJsonUri = workspaceFolder.toResource(EXTENSIONS_CONFIG);
|
||||
|
||||
return Promise.resolve(this.fileService.resolve(extensionsJsonUri)
|
||||
.then(() => this.fileService.resolveContent(extensionsJsonUri))
|
||||
.then(content => <IExtensionsConfigContent>json.parse(content.value), err => null));
|
||||
.then(() => this.fileService.readFile(extensionsJsonUri))
|
||||
.then(content => <IExtensionsConfigContent>json.parse(content.value.toString()), err => null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -968,7 +970,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
|
||||
|
||||
const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations';
|
||||
const workspaceUri = this.contextService.getWorkspace().folders[0].uri;
|
||||
return Promise.all([getHashedRemotesFromUri(workspaceUri, this.fileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => {
|
||||
return Promise.all([getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => {
|
||||
const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []);
|
||||
if (!hashedRemotes.length) {
|
||||
return undefined;
|
||||
|
||||
@@ -252,6 +252,30 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
default: ExtensionsPolicy.allowAll
|
||||
},
|
||||
'extensions.extensionKind': {
|
||||
type: 'object',
|
||||
description: localize('extensions.extensionKind', "Configure ui or workspace extensions and allow them to run locally or remotely in a remote window."),
|
||||
properties: {
|
||||
'ui': {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
pattern: '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$',
|
||||
}
|
||||
},
|
||||
'workspace': {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
pattern: '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$',
|
||||
}
|
||||
}
|
||||
},
|
||||
default: {
|
||||
ui: [],
|
||||
workspace: []
|
||||
}
|
||||
},
|
||||
'extensions.showInstalledExtensionsByDefault': {
|
||||
type: 'boolean',
|
||||
description: localize('extensions.showInstalledExtensionsByDefault', "When enabled, extensions view shows installed extensions view by default."),
|
||||
|
||||
@@ -23,7 +23,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
|
||||
import { ShowViewletAction } from 'vs/workbench/browser/viewlet';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery';
|
||||
import { IFileService, IContent } from 'vs/platform/files/common/files';
|
||||
import { IFileService, IFileContent } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
@@ -168,8 +168,7 @@ export class InstallAction extends ExtensionAction {
|
||||
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
|
||||
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@ILabelService private readonly labelService: ILabelService
|
||||
) {
|
||||
super(`extensions.install`, InstallAction.INSTALL_LABEL, InstallAction.Class, false);
|
||||
this.update();
|
||||
@@ -196,12 +195,12 @@ export class InstallAction extends ExtensionAction {
|
||||
} else {
|
||||
if (this._manifest && this.workbenchEnvironmentService.configuration.remoteAuthority) {
|
||||
if (isUIExtension(this._manifest, this.configurationService)) {
|
||||
this.label = `${InstallAction.INSTALL_LABEL} (${this.extensionManagementServerService.localExtensionManagementServer.label})`;
|
||||
this.tooltip = `${InstallAction.INSTALL_LABEL} (${this.extensionManagementServerService.localExtensionManagementServer.label})`;
|
||||
this.label = `${InstallAction.INSTALL_LABEL} ${localize('locally', "Locally")}`;
|
||||
this.tooltip = `${InstallAction.INSTALL_LABEL} ${localize('locally', "Locally")}`;
|
||||
} else {
|
||||
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority) || localize('remote', "Remote");
|
||||
this.label = `${InstallAction.INSTALL_LABEL} (${host})`;
|
||||
this.tooltip = `${InstallAction.INSTALL_LABEL} (${host})`;
|
||||
this.label = `${InstallAction.INSTALL_LABEL} on ${host}`;
|
||||
this.tooltip = `${InstallAction.INSTALL_LABEL} on ${host}`;
|
||||
}
|
||||
} else {
|
||||
this.label = InstallAction.INSTALL_LABEL;
|
||||
@@ -319,7 +318,7 @@ export class RemoteInstallAction extends ExtensionAction {
|
||||
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
|
||||
if (remoteAuthority) {
|
||||
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority) || localize('remote', "Remote");
|
||||
this.label = `${RemoteInstallAction.INSTALL_LABEL} (${host})`;
|
||||
this.label = `${RemoteInstallAction.INSTALL_LABEL} on ${host}`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1233,13 +1232,21 @@ export class ReloadAction extends ExtensionAction {
|
||||
const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
|
||||
if (runningExtension) {
|
||||
// Extension is running
|
||||
const isSameVersionRunning = isSameExtensionRunning && this.extension.version === runningExtension.version;
|
||||
if (isEnabled) {
|
||||
if (!isSameVersionRunning && !this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
|
||||
this.enabled = true;
|
||||
this.label = localize('reloadRequired', "Reload Required");
|
||||
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
|
||||
this.tooltip = localize('postUpdateTooltip', "Please reload Azure Data Studio to enable the updated extension.");
|
||||
if (!this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
|
||||
if (isSameExtensionRunning) {
|
||||
if (this.extension.version !== runningExtension.version) {
|
||||
this.enabled = true;
|
||||
this.label = localize('reloadRequired', "Reload Required");
|
||||
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
|
||||
this.tooltip = localize('postUpdateTooltip', "Please reload Azure Data Studio to enable the updated extension.");
|
||||
}
|
||||
} else {
|
||||
this.enabled = true;
|
||||
this.label = localize('reloadRequired', "Reload Required");
|
||||
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
|
||||
this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isSameExtensionRunning) {
|
||||
@@ -2057,7 +2064,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
|
||||
|
||||
protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<any> {
|
||||
return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
|
||||
.then(content => this.getSelectionPosition(content.value, content.resource, ['extensions', 'recommendations']))
|
||||
.then(content => this.getSelectionPosition(content.value.toString(), content.resource, ['extensions', 'recommendations']))
|
||||
.then(selection => this.editorService.openEditor({
|
||||
resource: workspaceConfigurationFile,
|
||||
options: {
|
||||
@@ -2071,7 +2078,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
|
||||
return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
|
||||
.then(content => {
|
||||
const extensionIdLowerCase = extensionId.toLowerCase();
|
||||
const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value) || {})['extensions'] || {};
|
||||
const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value.toString()) || {})['extensions'] || {};
|
||||
let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || [];
|
||||
let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || [];
|
||||
|
||||
@@ -2131,26 +2138,26 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
|
||||
}
|
||||
|
||||
protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
|
||||
return Promise.resolve(this.fileService.resolveContent(extensionsFileResource))
|
||||
return Promise.resolve(this.fileService.readFile(extensionsFileResource))
|
||||
.then(content => {
|
||||
return (json.parse(content.value) || {})['extensions'] || {};
|
||||
return (json.parse(content.value.toString()) || {})['extensions'] || {};
|
||||
}, err => ({ recommendations: [], unwantedRecommendations: [] }));
|
||||
}
|
||||
|
||||
protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
|
||||
return Promise.resolve(this.fileService.resolveContent(extensionsFileResource))
|
||||
return Promise.resolve(this.fileService.readFile(extensionsFileResource))
|
||||
.then(content => {
|
||||
return (<IExtensionsConfigContent>json.parse(content.value));
|
||||
return (<IExtensionsConfigContent>json.parse(content.value.toString()));
|
||||
}, err => ({ recommendations: [], unwantedRecommendations: [] }));
|
||||
}
|
||||
|
||||
private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<IContent> {
|
||||
return Promise.resolve(this.fileService.resolveContent(workspaceConfigurationFile))
|
||||
private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<IFileContent> {
|
||||
return Promise.resolve(this.fileService.readFile(workspaceConfigurationFile))
|
||||
.then(content => {
|
||||
const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value)['extensions'];
|
||||
const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value.toString())['extensions'];
|
||||
if (!workspaceRecommendations || !workspaceRecommendations.recommendations) {
|
||||
return this.jsonEditingService.write(workspaceConfigurationFile, { key: 'extensions', value: { recommendations: [] } }, true)
|
||||
.then(() => this.fileService.resolveContent(workspaceConfigurationFile));
|
||||
.then(() => this.fileService.readFile(workspaceConfigurationFile));
|
||||
}
|
||||
return content;
|
||||
});
|
||||
@@ -2179,8 +2186,8 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
|
||||
}
|
||||
|
||||
private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean, extensionsFileResource: URI, content: string }> {
|
||||
return Promise.resolve(this.fileService.resolveContent(extensionsFileResource)).then(content => {
|
||||
return { created: false, extensionsFileResource, content: content.value };
|
||||
return Promise.resolve(this.fileService.readFile(extensionsFileResource)).then(content => {
|
||||
return { created: false, extensionsFileResource, content: content.value.toString() };
|
||||
}, err => {
|
||||
return this.textFileService.write(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {
|
||||
return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };
|
||||
@@ -2562,29 +2569,42 @@ export class DisabledLabelAction extends ExtensionAction {
|
||||
|
||||
updateWhenCounterExtensionChanges: boolean = true;
|
||||
private disposables: IDisposable[] = [];
|
||||
private _runningExtensions: IExtensionDescription[] | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly warningAction: SystemDisabledWarningAction,
|
||||
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
) {
|
||||
super('extensions.disabledLabel', warningAction.tooltip, `${DisabledLabelAction.Class} hide`, false);
|
||||
warningAction.onDidChange(() => this.update(), this, this.disposables);
|
||||
this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this, this.disposables);
|
||||
this.updateRunningExtensions();
|
||||
}
|
||||
|
||||
private updateRunningExtensions(): void {
|
||||
this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
|
||||
}
|
||||
|
||||
update(): void {
|
||||
this.class = `${DisabledLabelAction.Class} hide`;
|
||||
this.label = '';
|
||||
this.enabled = false;
|
||||
if (this.warningAction.enabled) {
|
||||
this.enabled = true;
|
||||
this.class = DisabledLabelAction.Class;
|
||||
this.label = this.warningAction.tooltip;
|
||||
return;
|
||||
}
|
||||
if (this.extension && this.extension.local && !this.extensionEnablementService.isEnabled(this.extension.local)) {
|
||||
this.enabled = true;
|
||||
this.class = DisabledLabelAction.Class;
|
||||
this.label = localize('disabled by user', "This extension is disabled by the user.");
|
||||
return;
|
||||
if (this.extension && this.extension.local && this._runningExtensions) {
|
||||
const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
|
||||
const isExtensionRunning = this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier));
|
||||
if (!isExtensionRunning && !isEnabled) {
|
||||
this.enabled = true;
|
||||
this.class = DisabledLabelAction.Class;
|
||||
this.label = localize('disabled by user', "This extension is disabled by the user.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2630,31 +2650,37 @@ export class SystemDisabledWarningAction extends ExtensionAction {
|
||||
this.class = `${SystemDisabledWarningAction.Class} hide`;
|
||||
this.tooltip = '';
|
||||
if (this.extension && this.extension.local && this.extension.server && this._runningExtensions && this.workbenchEnvironmentService.configuration.remoteAuthority && this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
if (
|
||||
// Local Workspace Extension
|
||||
this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
|
||||
) {
|
||||
this.enabled = true;
|
||||
this.class = `${SystemDisabledWarningAction.Class}`;
|
||||
this.tooltip = localize('disabled workspace Extension', "This extension from {0} server is disabled because it cannot run in a window connected to the remote server.", this.getServerLabel(this.extensionManagementServerService.localExtensionManagementServer));
|
||||
if (!this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
|
||||
&& this.extensionsWorkbenchService.canInstall(this.extension)
|
||||
) {
|
||||
// Extension does not exist in remote
|
||||
this.tooltip = `${this.tooltip} ${localize('Install in remote server', "Install it in {0} server to enable.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer))}`;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
|
||||
const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null;
|
||||
if (
|
||||
// Not same as running extension
|
||||
runningExtensionServer && this.extension.server !== runningExtensionServer
|
||||
) {
|
||||
this.enabled = true;
|
||||
this.class = `${SystemDisabledWarningAction.Class}`;
|
||||
this.tooltip = localize('disabled because running in another server', "This extension from {0} server is disabled because another instance of same extension from {1} server is enabled.", this.getServerLabel(this.extension.server), this.getServerLabel(runningExtensionServer));
|
||||
return;
|
||||
const localExtension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
|
||||
const localExtensionServer = localExtension ? localExtension.server : null;
|
||||
if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)) {
|
||||
if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
this.enabled = true;
|
||||
this.class = `${SystemDisabledWarningAction.Class}`;
|
||||
this.tooltip = localize('disabled locally', "Extension is enabled on '{0}' and disabled locally.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
|
||||
return;
|
||||
}
|
||||
if (localExtensionServer !== this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
this.enabled = true;
|
||||
this.class = `${SystemDisabledWarningAction.Class}`;
|
||||
this.tooltip = localize('Install in remote server', "Install the extension on '{0}' to enable.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && isUIExtension(this.extension.local.manifest, this.configurationService)) {
|
||||
if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {
|
||||
this.enabled = true;
|
||||
this.class = `${SystemDisabledWarningAction.Class}`;
|
||||
this.tooltip = localize('disabled remotely', "Extension is enabled locally and disabled on '{0}'.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
|
||||
return;
|
||||
}
|
||||
if (localExtensionServer !== this.extensionManagementServerService.localExtensionManagementServer) {
|
||||
this.enabled = true;
|
||||
this.class = `${SystemDisabledWarningAction.Class}`;
|
||||
this.tooltip = localize('Install in local server', "Install the extension locally to enable.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2878,7 +2904,7 @@ export class InstallVSIXAction extends Action {
|
||||
this.extensionsWorkbenchService.install(vsix).then(extension => {
|
||||
const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
|
||||
const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Azure Data Studio to complete installing the extension {0}.", extension.identifier.id)
|
||||
: localize('InstallVSIXAction.success', "Installing the extension {0} is completed.", extension.identifier.id);
|
||||
: localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.identifier.id);
|
||||
const actions = requireReload ? [{
|
||||
label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
|
||||
run: () => this.windowService.reloadWindow()
|
||||
@@ -2911,7 +2937,7 @@ export class InstallVSIXAction extends Action {
|
||||
this.extensionsWorkbenchService.install(vsix).then(extension => {
|
||||
const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
|
||||
const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Azure Data Studio to complete installing the extension {0}.", extension.identifier.id)
|
||||
: localize('InstallVSIXAction.success', "Installing the extension {0} is completed.", extension.identifier.id);
|
||||
: localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.identifier.id);
|
||||
const actions = requireReload ? [{
|
||||
label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
|
||||
run: () => this.windowService.reloadWindow()
|
||||
|
||||
@@ -66,7 +66,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
const element = append(root, $('.extension'));
|
||||
const iconContainer = append(element, $('.icon-container'));
|
||||
const icon = append(iconContainer, $<HTMLImageElement>('img.icon'));
|
||||
const badgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer);
|
||||
const iconRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer);
|
||||
const details = append(element, $('.details'));
|
||||
const headerContainer = append(details, $('.header-container'));
|
||||
const header = append(headerContainer, $('.header'));
|
||||
@@ -74,6 +74,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
const version = append(header, $('span.version'));
|
||||
const installCount = append(header, $('span.install-count'));
|
||||
const ratings = append(header, $('span.ratings'));
|
||||
const headerRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, header);
|
||||
const description = append(details, $('.description.ellipsis'));
|
||||
const footer = append(details, $('.footer'));
|
||||
const author = append(footer, $('.author.ellipsis'));
|
||||
@@ -89,10 +90,11 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
actionbar.onDidRun(({ error }) => error && this.notificationService.error(error));
|
||||
|
||||
const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction);
|
||||
const reloadAction = this.instantiationService.createInstance(ReloadAction);
|
||||
const actions = [
|
||||
this.instantiationService.createInstance(StatusLabelAction),
|
||||
this.instantiationService.createInstance(UpdateAction),
|
||||
this.instantiationService.createInstance(ReloadAction),
|
||||
reloadAction,
|
||||
this.instantiationService.createInstance(InstallAction),
|
||||
this.instantiationService.createInstance(RemoteInstallAction),
|
||||
this.instantiationService.createInstance(MaliciousStatusLabelAction, false),
|
||||
@@ -100,10 +102,11 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
this.instantiationService.createInstance(ManageExtensionAction)
|
||||
];
|
||||
const disabledLabelAction = this.instantiationService.createInstance(DisabledLabelAction, systemDisabledWarningAction);
|
||||
const tooltipWidget = this.instantiationService.createInstance(TooltipWidget, root, disabledLabelAction, recommendationWidget);
|
||||
const tooltipWidget = this.instantiationService.createInstance(TooltipWidget, root, disabledLabelAction, recommendationWidget, reloadAction);
|
||||
const widgets = [
|
||||
recommendationWidget,
|
||||
badgeWidget,
|
||||
iconRemoteBadgeWidget,
|
||||
headerRemoteBadgeWidget,
|
||||
tooltipWidget,
|
||||
this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version),
|
||||
this.instantiationService.createInstance(InstallCountWidget, installCount, true),
|
||||
|
||||
@@ -11,13 +11,14 @@ import * as platform from 'vs/base/common/platform';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { extensionButtonProminentBackground, extensionButtonProminentForeground, DisabledLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
|
||||
import { extensionButtonProminentBackground, extensionButtonProminentForeground, DisabledLabelAction, ReloadAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
|
||||
private _extension: IExtension;
|
||||
@@ -148,11 +149,15 @@ export class TooltipWidget extends ExtensionWidget {
|
||||
constructor(
|
||||
private readonly parent: HTMLElement,
|
||||
private readonly extensionLabelAction: DisabledLabelAction,
|
||||
private readonly recommendationWidget: RecommendationWidget
|
||||
private readonly recommendationWidget: RecommendationWidget,
|
||||
private readonly reloadAction: ReloadAction
|
||||
) {
|
||||
super();
|
||||
this._register(this.extensionLabelAction.onDidChange(() => this.render()));
|
||||
this._register(this.recommendationWidget.onDidChangeTooltip(() => this.render()));
|
||||
this._register(Event.any<any>(
|
||||
this.extensionLabelAction.onDidChange,
|
||||
this.reloadAction.onDidChange,
|
||||
this.recommendationWidget.onDidChangeTooltip
|
||||
)(() => this.render()));
|
||||
}
|
||||
|
||||
render(): void {
|
||||
@@ -166,6 +171,9 @@ export class TooltipWidget extends ExtensionWidget {
|
||||
}
|
||||
|
||||
private getTitle(): string {
|
||||
if (this.reloadAction.enabled) {
|
||||
return this.reloadAction.tooltip;
|
||||
}
|
||||
if (this.extensionLabelAction.enabled) {
|
||||
return this.extensionLabelAction.label;
|
||||
}
|
||||
@@ -235,30 +243,30 @@ export class RecommendationWidget extends ExtensionWidget {
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class RemoteBadgeWidget extends ExtensionWidget {
|
||||
|
||||
private element: HTMLElement | null;
|
||||
private remoteBadge: RemoteBadge | null;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
private element: HTMLElement;
|
||||
|
||||
constructor(
|
||||
private parent: HTMLElement,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
parent: HTMLElement,
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
this.element = append(parent, $('.extension-remote-badge-container'));
|
||||
this.render();
|
||||
this._register(toDisposable(() => this.clear()));
|
||||
}
|
||||
|
||||
private clear(): void {
|
||||
if (this.element) {
|
||||
this.parent.removeChild(this.element);
|
||||
if (this.remoteBadge) {
|
||||
this.element.removeChild(this.remoteBadge.element);
|
||||
this.remoteBadge.dispose();
|
||||
}
|
||||
this.element = null;
|
||||
this.remoteBadge = null;
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
|
||||
@@ -268,30 +276,56 @@ export class RemoteBadgeWidget extends ExtensionWidget {
|
||||
return;
|
||||
}
|
||||
if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
this.element = append(this.parent, $('div.extension-remote-badge'));
|
||||
append(this.element, $('span.octicon.octicon-remote'));
|
||||
|
||||
const applyBadgeStyle = () => {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND);
|
||||
const fgColor = this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY ? this.themeService.getTheme().getColor(STATUS_BAR_NO_FOLDER_FOREGROUND) : this.themeService.getTheme().getColor(STATUS_BAR_FOREGROUND);
|
||||
this.element.style.backgroundColor = bgColor ? bgColor.toString() : '';
|
||||
this.element.style.color = fgColor ? fgColor.toString() : '';
|
||||
};
|
||||
applyBadgeStyle();
|
||||
this.themeService.onThemeChange(applyBadgeStyle, this, this.disposables);
|
||||
this.workspaceContextService.onDidChangeWorkbenchState(applyBadgeStyle, this, this.disposables);
|
||||
|
||||
const updateTitle = () => {
|
||||
if (this.element) {
|
||||
this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority));
|
||||
}
|
||||
};
|
||||
this.labelService.onDidChangeFormatters(() => updateTitle(), this, this.disposables);
|
||||
updateTitle();
|
||||
this.remoteBadge = this.instantiationService.createInstance(RemoteBadge);
|
||||
append(this.element, this.remoteBadge.element);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.remoteBadge) {
|
||||
this.remoteBadge.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteBadge extends Disposable {
|
||||
|
||||
readonly element: HTMLElement;
|
||||
|
||||
constructor(
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super();
|
||||
this.element = $('div.extension-remote-badge');
|
||||
this.render();
|
||||
}
|
||||
|
||||
private render(): void {
|
||||
append(this.element, $('span.octicon.octicon-remote'));
|
||||
|
||||
const applyBadgeStyle = () => {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND);
|
||||
const fgColor = this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY ? this.themeService.getTheme().getColor(STATUS_BAR_NO_FOLDER_FOREGROUND) : this.themeService.getTheme().getColor(STATUS_BAR_FOREGROUND);
|
||||
this.element.style.backgroundColor = bgColor ? bgColor.toString() : '';
|
||||
this.element.style.color = fgColor ? fgColor.toString() : '';
|
||||
};
|
||||
applyBadgeStyle();
|
||||
this._register(this.themeService.onThemeChange(() => applyBadgeStyle()));
|
||||
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => applyBadgeStyle()));
|
||||
|
||||
const updateTitle = () => {
|
||||
if (this.element) {
|
||||
this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority));
|
||||
}
|
||||
};
|
||||
this._register(this.labelService.onDidChangeFormatters(() => updateTitle()));
|
||||
updateTitle();
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,17 @@
|
||||
.extension-editor > .header > .icon-container .extension-remote-badge {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 94px;
|
||||
top: 88px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.extension-editor > .header > .icon-container .extension-remote-badge .octicon {
|
||||
font-size: 32px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.extension-editor > .header > .details {
|
||||
|
||||
@@ -102,10 +102,32 @@
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container > .extension-remote-badge {
|
||||
.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container .extension-remote-badge {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header > .extension-remote-badge-container {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header .extension-remote-badge {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header .extension-remote-badge > .octicon {
|
||||
font-size: 13px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.extensions-viewlet.narrow > .extensions .extension > .icon-container,
|
||||
@@ -146,10 +168,13 @@
|
||||
opacity: 0.85;
|
||||
font-size: 80%;
|
||||
padding-left: 6px;
|
||||
flex: 1;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.extensions-viewlet:not(.narrow) > .extensions .extension > .details > .header-container > .header > .version {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count:not(:empty) {
|
||||
font-size: 80%;
|
||||
margin: 0 6px;
|
||||
@@ -164,6 +189,7 @@
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.extensions-viewlet:not(.narrow) > .extensions .extension > .details > .header-container > .header > .extension-remote-badge-container,
|
||||
.extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .ratings,
|
||||
.extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .install-count {
|
||||
display: none;
|
||||
|
||||
@@ -46,12 +46,4 @@
|
||||
|
||||
.extension-ratings.small > .count {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.extension-remote-badge {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -254,7 +254,7 @@ class Extension implements IExtension {
|
||||
}
|
||||
|
||||
if (this.local && this.local.readmeUrl) {
|
||||
return this.fileService.resolveContent(this.local.readmeUrl, { encoding: 'utf8' }).then(content => content.value);
|
||||
return this.fileService.readFile(this.local.readmeUrl).then(content => content.value.toString());
|
||||
}
|
||||
|
||||
if (this.type === ExtensionType.System) {
|
||||
@@ -297,7 +297,7 @@ ${this.description}
|
||||
return Promise.reject(new Error('not available'));
|
||||
}
|
||||
|
||||
return this.fileService.resolveContent(changelogUrl, { encoding: 'utf8' }).then(content => content.value);
|
||||
return this.fileService.readFile(changelogUrl).then(content => content.value.toString());
|
||||
}
|
||||
|
||||
get dependencies(): string[] {
|
||||
|
||||
@@ -21,12 +21,11 @@ import { Emitter } from 'vs/base/common/event';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestTextResourceConfigurationService, TestContextService, TestLifecycleService, TestEnvironmentService, TestStorageService, TestSharedProcessService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestContextService, TestLifecycleService, TestSharedProcessService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
import { LegacyFileService } from 'vs/workbench/services/files/node/fileService';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { IPager } from 'vs/base/common/paging';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
@@ -47,6 +46,11 @@ import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/ele
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
|
||||
import { FileService } from 'vs/workbench/services/files/common/fileService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
const mockExtensionGallery: IGalleryExtension[] = [
|
||||
aGalleryExtension('MockExtension1', {
|
||||
|
||||
@@ -12,9 +12,9 @@ import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
/**
|
||||
* An implementation of editor for binary files like images.
|
||||
@@ -26,10 +26,10 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IStorageService storageService: IStorageService
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITextFileService textFileService: ITextFileService
|
||||
) {
|
||||
super(
|
||||
BinaryFileEditor.ID,
|
||||
@@ -39,7 +39,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
|
||||
},
|
||||
telemetryService,
|
||||
themeService,
|
||||
fileService,
|
||||
textFileService,
|
||||
storageService
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { isValidBasename } from 'vs/base/common/extpath';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileEditorModel, ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import { EditorOptions, TextEditorOptions, IEditorCloseEvent } from 'vs/workbench/common/editor';
|
||||
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
|
||||
@@ -170,7 +170,7 @@ export class TextFileEditor extends BaseTextEditor {
|
||||
// In case we tried to open a file inside the text editor and the response
|
||||
// indicates that this is not a text file, reopen the file through the binary
|
||||
// editor.
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY) {
|
||||
if ((<TextFileOperationError>error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY) {
|
||||
return this.openAsBinary(input, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IEditorInputFactory, EditorInput, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
|
||||
import { AutoSaveConfiguration, HotExitConfiguration, SUPPORTED_ENCODINGS } from 'vs/platform/files/common/files';
|
||||
import { AutoSaveConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files';
|
||||
import { VIEWLET_ID, SortOrderConfiguration, FILE_EDITOR_INPUT_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files';
|
||||
import { FileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/fileEditorTracker';
|
||||
import { SaveErrorHandler } from 'vs/workbench/contrib/files/browser/saveErrorHandler';
|
||||
@@ -37,6 +37,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ExplorerService } from 'vs/workbench/contrib/files/common/explorerService';
|
||||
import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
// Viewlet Action
|
||||
export class OpenExplorerViewletAction extends ShowViewletAction {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { basename } from 'vs/base/common/resources';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel, IResolvedTextFileEditorModel, IWriteTextFileOptions } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
@@ -134,7 +134,7 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I
|
||||
// Any other save error
|
||||
else {
|
||||
const isReadonly = fileOperationError.fileOperationResult === FileOperationResult.FILE_READ_ONLY;
|
||||
const triedToMakeWriteable = isReadonly && fileOperationError.options && fileOperationError.options.overwriteReadonly;
|
||||
const triedToMakeWriteable = isReadonly && fileOperationError.options && (fileOperationError.options as IWriteTextFileOptions).overwriteReadonly;
|
||||
const isPermissionDenied = fileOperationError.fileOperationResult === FileOperationResult.FILE_PERMISSION_DENIED;
|
||||
|
||||
// Save Elevated (TODO@remote cannot write elevated https://github.com/Microsoft/vscode/issues/48659)
|
||||
|
||||
@@ -12,7 +12,7 @@ import { EncodingMode, ConfirmResult, EditorInput, IFileEditorInput, ITextEditor
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
|
||||
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent, LoadReason } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent, LoadReason, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IReference } from 'vs/base/common/lifecycle';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
@@ -269,7 +269,10 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
||||
}, error => {
|
||||
|
||||
// In case of an error that indicates that the file is binary or too large, just return with the binary editor model
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY || (<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE) {
|
||||
if (
|
||||
(<TextFileOperationError>error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY ||
|
||||
(<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE
|
||||
) {
|
||||
return this.doResolveAsBinary();
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
|
||||
private resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
|
||||
const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority);
|
||||
|
||||
return this.textFileService.resolve(savedFileResource).then(content => {
|
||||
return this.textFileService.readStream(savedFileResource).then(content => {
|
||||
let codeEditorModel = this.modelService.getModel(resource);
|
||||
if (codeEditorModel) {
|
||||
this.modelService.updateModel(codeEditorModel, content.value);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { EncodingMode, Verbosity } from 'vs/workbench/common/editor';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
@@ -155,7 +155,7 @@ suite('Files - FileEditorInput', () => {
|
||||
test('resolve handles binary files', function () {
|
||||
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined);
|
||||
|
||||
accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_IS_BINARY));
|
||||
accessor.textFileService.setResolveTextContentErrorOnce(new TextFileOperationError('error', TextFileOperationResult.FILE_IS_BINARY));
|
||||
|
||||
return input.resolve().then(resolved => {
|
||||
assert.ok(resolved);
|
||||
|
||||
@@ -9,8 +9,8 @@ import { toResource } from 'vs/base/test/common/utils';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { workbenchInstantiationService, TestTextFileService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITextFileService, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { FileChangesEvent, FileChangeType, IFileService, snapshotToString } from 'vs/platform/files/common/files';
|
||||
import { ITextFileService, IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
|
||||
@@ -511,20 +511,20 @@ export class SettingsTargetsWidget extends Widget {
|
||||
actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : undefined
|
||||
}));
|
||||
|
||||
this.userLocalSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL));
|
||||
this.userLocalSettings = new Action('userSettings', localize('userSettings', "User"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL));
|
||||
this.userLocalSettings.tooltip = this.userLocalSettings.label;
|
||||
|
||||
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
|
||||
const hostLabel = remoteAuthority && this.labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAuthority);
|
||||
const remoteSettingsLabel = localize('userSettingsRemote', "Remote Settings") +
|
||||
const remoteSettingsLabel = localize('userSettingsRemote', "Remote") +
|
||||
(hostLabel ? ` (${hostLabel})` : '');
|
||||
this.userRemoteSettings = new Action('userSettingsRemote', remoteSettingsLabel, '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_REMOTE));
|
||||
this.userRemoteSettings.tooltip = this.userRemoteSettings.label;
|
||||
|
||||
this.workspaceSettings = new Action('workspaceSettings', localize('workspaceSettings', "Workspace Settings"), '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE));
|
||||
this.workspaceSettings = new Action('workspaceSettings', localize('workspaceSettings', "Workspace"), '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE));
|
||||
this.workspaceSettings.tooltip = this.workspaceSettings.label;
|
||||
|
||||
const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder Settings"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder.uri));
|
||||
const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder.uri));
|
||||
this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionItem, folderSettingsAction);
|
||||
|
||||
this.update();
|
||||
@@ -551,14 +551,14 @@ export class SettingsTargetsWidget extends Widget {
|
||||
|
||||
setResultCount(settingsTarget: SettingsTarget, count: number): void {
|
||||
if (settingsTarget === ConfigurationTarget.WORKSPACE) {
|
||||
let label = localize('workspaceSettings', "Workspace Settings");
|
||||
let label = localize('workspaceSettings', "Workspace");
|
||||
if (count) {
|
||||
label += ` (${count})`;
|
||||
}
|
||||
|
||||
this.workspaceSettings.label = label;
|
||||
} else if (settingsTarget === ConfigurationTarget.USER_LOCAL) {
|
||||
let label = localize('userSettings', "User Settings");
|
||||
let label = localize('userSettings', "User");
|
||||
if (count) {
|
||||
label += ` (${count})`;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { IFolderQuery, IPatternInfo, QueryType, ITextQuery, IFileQuery } from 'vs/workbench/services/search/common/search';
|
||||
import { IWorkspaceContextService, toWorkspaceFolders, Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, toWorkspaceFolder, Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
|
||||
import { ISearchPathsInfo, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { TestContextService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
|
||||
|
||||
@@ -24,6 +24,7 @@ suite('QueryBuilder', () => {
|
||||
const PATTERN_INFO: IPatternInfo = { pattern: 'a' };
|
||||
const ROOT_1 = fixPath('/foo/root1');
|
||||
const ROOT_1_URI = getUri(ROOT_1);
|
||||
const WS_CONFIG_PATH = getUri('/bar/test.code-workspace'); // location of the workspace file (not important except that it is a file URI)
|
||||
|
||||
let instantiationService: TestInstantiationService;
|
||||
let queryBuilder: QueryBuilder;
|
||||
@@ -40,7 +41,7 @@ suite('QueryBuilder', () => {
|
||||
instantiationService.stub(IConfigurationService, mockConfigService);
|
||||
|
||||
mockContextService = new TestContextService();
|
||||
mockWorkspace = new Workspace('workspace', toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }]));
|
||||
mockWorkspace = new Workspace('workspace', [toWorkspaceFolder(ROOT_1_URI)]);
|
||||
mockContextService.setWorkspace(mockWorkspace);
|
||||
|
||||
instantiationService.stub(IWorkspaceContextService, mockContextService);
|
||||
@@ -277,7 +278,7 @@ suite('QueryBuilder', () => {
|
||||
const ROOT_2_URI = getUri(ROOT_2);
|
||||
const ROOT_3 = fixPath('/project/root3');
|
||||
const ROOT_3_URI = getUri(ROOT_3);
|
||||
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: ROOT_2_URI.fsPath }, { path: ROOT_3_URI.fsPath }]);
|
||||
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: ROOT_2_URI.fsPath }, { path: ROOT_3_URI.fsPath }], WS_CONFIG_PATH);
|
||||
mockWorkspace.configuration = uri.file(fixPath('/config'));
|
||||
|
||||
mockConfigService.setUserConfiguration('search', {
|
||||
@@ -689,7 +690,7 @@ suite('QueryBuilder', () => {
|
||||
|
||||
test('relative includes w/two root folders', () => {
|
||||
const ROOT_2 = '/project/root2';
|
||||
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }]);
|
||||
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }], WS_CONFIG_PATH);
|
||||
mockWorkspace.configuration = uri.file(fixPath('config'));
|
||||
|
||||
const cases: [string, ISearchPathsInfo][] = [
|
||||
@@ -730,7 +731,7 @@ suite('QueryBuilder', () => {
|
||||
test('include ./foldername', () => {
|
||||
const ROOT_2 = '/project/root2';
|
||||
const ROOT_1_FOLDERNAME = 'foldername';
|
||||
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath, name: ROOT_1_FOLDERNAME }, { path: getUri(ROOT_2).fsPath }]);
|
||||
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath, name: ROOT_1_FOLDERNAME }, { path: getUri(ROOT_2).fsPath }], WS_CONFIG_PATH);
|
||||
mockWorkspace.configuration = uri.file(fixPath('config'));
|
||||
|
||||
const cases: [string, ISearchPathsInfo][] = [
|
||||
@@ -758,7 +759,7 @@ suite('QueryBuilder', () => {
|
||||
test('relative includes w/multiple ambiguous root folders', () => {
|
||||
const ROOT_2 = '/project/rootB';
|
||||
const ROOT_3 = '/otherproject/rootB';
|
||||
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }, { path: getUri(ROOT_3).fsPath }]);
|
||||
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }, { path: getUri(ROOT_3).fsPath }], WS_CONFIG_PATH);
|
||||
mockWorkspace.configuration = uri.file(fixPath('/config'));
|
||||
|
||||
const cases: [string, ISearchPathsInfo][] = [
|
||||
|
||||
@@ -198,7 +198,7 @@ export class SnippetFile {
|
||||
|
||||
load(): Promise<this> {
|
||||
if (!this._loadPromise) {
|
||||
this._loadPromise = Promise.resolve(this._fileService.resolveContent(this.location, { encoding: 'utf8' })).then(content => {
|
||||
this._loadPromise = Promise.resolve(this._fileService.readFile(this.location)).then(content => {
|
||||
const data = <JsonSerializedSnippets>jsonParse(content.value.toString());
|
||||
if (typeof data === 'object') {
|
||||
forEach(data, entry => {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { localize } from 'vs/nls';
|
||||
import * as crypto from 'crypto';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService, IFileStat, IResolveFileResult, IContent } from 'vs/platform/files/common/files';
|
||||
import { IFileService, IFileStat, IResolveFileResult } from 'vs/platform/files/common/files';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
@@ -20,6 +20,7 @@ import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspa
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { ITextFileService, ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/;
|
||||
const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/;
|
||||
@@ -193,14 +194,14 @@ export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: bool
|
||||
});
|
||||
}
|
||||
|
||||
export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, stripEndingDotGit: boolean = false): Promise<string[]> {
|
||||
export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, textFileService: ITextFileService, stripEndingDotGit: boolean = false): Promise<string[]> {
|
||||
const path = workspaceUri.path;
|
||||
const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` });
|
||||
return fileService.exists(uri).then(exists => {
|
||||
if (!exists) {
|
||||
return [];
|
||||
}
|
||||
return fileService.resolveContent(uri, { acceptTextOnly: true }).then(
|
||||
return textFileService.read(uri, { acceptTextOnly: true }).then(
|
||||
content => getHashedRemotesFromConfig(content.value, stripEndingDotGit),
|
||||
err => [] // ignore missing or binary file
|
||||
);
|
||||
@@ -221,7 +222,8 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IStorageService private readonly storageService: IStorageService
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService
|
||||
) {
|
||||
this.report();
|
||||
}
|
||||
@@ -434,7 +436,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
tags['workspace.android.cpp'] = true;
|
||||
}
|
||||
|
||||
function getFilePromises(filename: string, fileService: IFileService, contentHandler: (content: IContent) => void): Promise<void>[] {
|
||||
function getFilePromises(filename: string, fileService: IFileService, textFileService: ITextFileService, contentHandler: (content: ITextFileContent) => void): Promise<void>[] {
|
||||
return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => {
|
||||
const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` });
|
||||
return fileService.exists(uri).then(exists => {
|
||||
@@ -442,7 +444,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return fileService.resolveContent(uri, { acceptTextOnly: true }).then(contentHandler);
|
||||
return textFileService.read(uri, { acceptTextOnly: true }).then(contentHandler);
|
||||
}, err => {
|
||||
// Ignore missing file
|
||||
});
|
||||
@@ -468,7 +470,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
}
|
||||
}
|
||||
|
||||
const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, content => {
|
||||
const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, this.textFileService, content => {
|
||||
const dependencies: string[] = content.value.split(/\r\n|\r|\n/);
|
||||
for (let dependency of dependencies) {
|
||||
// Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo`
|
||||
@@ -479,7 +481,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
}
|
||||
});
|
||||
|
||||
const pipfilePromises = getFilePromises('pipfile', this.fileService, content => {
|
||||
const pipfilePromises = getFilePromises('pipfile', this.fileService, this.textFileService, content => {
|
||||
let dependencies: string[] = content.value.split(/\r\n|\r|\n/);
|
||||
|
||||
// We're only interested in the '[packages]' section of the Pipfile
|
||||
@@ -499,7 +501,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
|
||||
});
|
||||
|
||||
const packageJsonPromises = getFilePromises('package.json', this.fileService, content => {
|
||||
const packageJsonPromises = getFilePromises('package.json', this.fileService, this.textFileService, content => {
|
||||
try {
|
||||
const packageJsonContents = JSON.parse(content.value);
|
||||
if (packageJsonContents['dependencies']) {
|
||||
@@ -624,7 +626,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
if (!exists) {
|
||||
return [];
|
||||
}
|
||||
return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then(
|
||||
return this.textFileService.read(uri, { acceptTextOnly: true }).then(
|
||||
content => getDomainsOfRemotes(content.value, SecondLevelDomainWhitelist),
|
||||
err => [] // ignore missing or binary file
|
||||
);
|
||||
@@ -644,7 +646,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
|
||||
private reportRemotes(workspaceUris: URI[]): void {
|
||||
Promise.all<string[]>(workspaceUris.map(workspaceUri => {
|
||||
return getHashedRemotesFromUri(workspaceUri, this.fileService, true);
|
||||
return getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true);
|
||||
})).then(hashedRemotes => {
|
||||
/* __GDPR__
|
||||
"workspace.hashedRemotes" : {
|
||||
@@ -693,7 +695,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then(
|
||||
return this.textFileService.read(uri, { acceptTextOnly: true }).then(
|
||||
content => !!content.value.match(/azure/i),
|
||||
err => false
|
||||
);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -14,6 +13,7 @@ import Severity from 'vs/base/common/severity';
|
||||
import { Terminal as XTermTerminal } from 'vscode-xterm';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { mergeDefaultShellPathAndArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
|
||||
const MINIMUM_FONT_SIZE = 6;
|
||||
const MAXIMUM_FONT_SIZE = 25;
|
||||
@@ -167,9 +167,9 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
return this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, defaultValue);
|
||||
}
|
||||
|
||||
public checkWorkspaceShellPermissions(platformOverride: platform.Platform = platform.platform): boolean {
|
||||
public checkWorkspaceShellPermissions(osOverride: platform.OperatingSystem = platform.OS): boolean {
|
||||
// Check whether there is a workspace setting
|
||||
const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux';
|
||||
const platformKey = osOverride === platform.OperatingSystem.Windows ? 'windows' : osOverride === platform.OperatingSystem.Macintosh ? 'osx' : 'linux';
|
||||
const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`);
|
||||
const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`);
|
||||
const envConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.env.${platformKey}`);
|
||||
@@ -228,28 +228,8 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
|
||||
}
|
||||
|
||||
public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride: platform.Platform = platform.platform): void {
|
||||
const isWorkspaceShellAllowed = this.checkWorkspaceShellPermissions(platformOverride);
|
||||
const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux';
|
||||
const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`);
|
||||
const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`);
|
||||
|
||||
shell.executable = (isWorkspaceShellAllowed ? shellConfigValue.value : shellConfigValue.user) || shellConfigValue.default;
|
||||
shell.args = (isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default;
|
||||
|
||||
// Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's
|
||||
// safe to assume that this was used by accident as Sysnative does not
|
||||
// exist and will break the terminal in non-WoW64 environments.
|
||||
if ((platformOverride === platform.Platform.Windows) && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') && process.env.windir) {
|
||||
const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase();
|
||||
if (shell.executable.toLowerCase().indexOf(sysnativePath) === 0) {
|
||||
shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert / to \ on Windows for convenience
|
||||
if (platformOverride === platform.Platform.Windows) {
|
||||
shell.executable = shell.executable.replace(/\//g, '\\');
|
||||
}
|
||||
const isWorkspaceShellAllowed = this.checkWorkspaceShellPermissions(platformOverride === platform.Platform.Windows ? platform.OperatingSystem.Windows : (platformOverride === platform.Platform.Mac ? platform.OperatingSystem.Macintosh : platform.OperatingSystem.Linux));
|
||||
mergeDefaultShellPathAndArgs(shell, (key) => this._workspaceConfigurationService.inspect(key), isWorkspaceShellAllowed);
|
||||
}
|
||||
|
||||
private _toInteger(source: any, minimum: number, maximum: number, fallback: number): number {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
@@ -22,6 +22,7 @@ import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
/** The amount of time to consider terminal errors to be related to the launch */
|
||||
const LAUNCHING_DURATION = 500;
|
||||
@@ -133,16 +134,13 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
}
|
||||
|
||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(hasRemoteAuthority ? REMOTE_HOST_SCHEME : undefined);
|
||||
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows);
|
||||
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper);
|
||||
} else {
|
||||
this._process = this._launchProcess(shellLaunchConfig, cols, rows);
|
||||
}
|
||||
this.processState = ProcessState.LAUNCHING;
|
||||
|
||||
// The process is non-null, but TS isn't clever enough to know
|
||||
const p = this._process!;
|
||||
|
||||
p.onProcessData(data => {
|
||||
this._process.onProcessData(data => {
|
||||
const beforeProcessDataEvent: IBeforeProcessDataEvent = { data };
|
||||
this._onBeforeProcessData.fire(beforeProcessDataEvent);
|
||||
if (beforeProcessDataEvent.data && beforeProcessDataEvent.data.length > 0) {
|
||||
@@ -150,19 +148,19 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
}
|
||||
});
|
||||
|
||||
p.onProcessIdReady(pid => {
|
||||
this._process.onProcessIdReady(pid => {
|
||||
this.shellProcessId = pid;
|
||||
this._onProcessReady.fire();
|
||||
|
||||
// Send any queued data that's waiting
|
||||
if (this._preLaunchInputQueue.length > 0) {
|
||||
p.input(this._preLaunchInputQueue.join(''));
|
||||
if (this._preLaunchInputQueue.length > 0 && this._process) {
|
||||
this._process.input(this._preLaunchInputQueue.join(''));
|
||||
this._preLaunchInputQueue.length = 0;
|
||||
}
|
||||
});
|
||||
|
||||
p.onProcessTitleChanged(title => this._onProcessTitle.fire(title));
|
||||
p.onProcessExit(exitCode => this._onExit(exitCode));
|
||||
this._process.onProcessTitleChanged(title => this._onProcessTitle.fire(title));
|
||||
this._process.onProcessExit(exitCode => this._onExit(exitCode));
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.processState === ProcessState.LAUNCHING) {
|
||||
@@ -175,32 +173,41 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
if (!shellLaunchConfig.executable) {
|
||||
this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
|
||||
}
|
||||
|
||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd);
|
||||
const env = this._createEnvironment(shellLaunchConfig, activeWorkspaceRootUri);
|
||||
|
||||
// Compel type system as process.env should not have any undefined entries
|
||||
this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env);
|
||||
return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty);
|
||||
}
|
||||
|
||||
private _createEnvironment(shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined): platform.IProcessEnvironment {
|
||||
// Create a terminal environment based on settings, launch config and permissions
|
||||
let env: platform.IProcessEnvironment = {};
|
||||
|
||||
if (shellLaunchConfig.strictEnv) {
|
||||
// Only base the terminal process environment on this environment and add the
|
||||
// various mixins when strictEnv is false
|
||||
env = { ...shellLaunchConfig.env } as any;
|
||||
// strictEnv is true, only use the requested env (ignoring null entries)
|
||||
terminalEnvironment.mergeNonNullKeys(env, shellLaunchConfig.env);
|
||||
} else {
|
||||
// Merge process env with the env from config and from shellLaunchConfig
|
||||
env = { ...process.env } as any;
|
||||
terminalEnvironment.mergeNonNullKeys(env, process.env);
|
||||
|
||||
// Resolve env vars from config and shell
|
||||
// Determine config env based on workspace shell permissions
|
||||
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null;
|
||||
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
|
||||
const envFromConfigValue = this._workspaceConfigurationService.inspect<{ [key: string]: string }>(`terminal.integrated.env.${platformKey}`);
|
||||
const allowedEnvFromConfig = (isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user);
|
||||
const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...allowedEnvFromConfig }, lastActiveWorkspaceRoot);
|
||||
const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
|
||||
shellLaunchConfig.env = envFromShell;
|
||||
const envFromConfigValue = this._workspaceConfigurationService.inspect<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
|
||||
const allowedEnvFromConfig = { ...(isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user) };
|
||||
|
||||
terminalEnvironment.mergeEnvironments(env, envFromConfig);
|
||||
// Resolve env vars from config and shell
|
||||
if (allowedEnvFromConfig) {
|
||||
terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, allowedEnvFromConfig, lastActiveWorkspaceRoot);
|
||||
}
|
||||
if (shellLaunchConfig.env) {
|
||||
terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, shellLaunchConfig.env, lastActiveWorkspaceRoot);
|
||||
}
|
||||
|
||||
// Merge config (settings) and ShellLaunchConfig environments
|
||||
terminalEnvironment.mergeEnvironments(env, allowedEnvFromConfig);
|
||||
terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
|
||||
|
||||
// Sanitize the environment, removing any undesirable VS Code and Electron environment
|
||||
@@ -210,9 +217,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
// Adding other env keys necessary to create the process
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables);
|
||||
}
|
||||
|
||||
this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env);
|
||||
return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty);
|
||||
return env;
|
||||
}
|
||||
|
||||
public setDimensions(cols: number, rows: number): void {
|
||||
|
||||
@@ -115,7 +115,7 @@ export interface ITerminalConfigHelper {
|
||||
mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride?: platform.Platform): void;
|
||||
/** Sets whether a workspace shell configuration is allowed or not */
|
||||
setWorkspaceShellAllowed(isAllowed: boolean): void;
|
||||
checkWorkspaceShellPermissions(platformOverride?: platform.Platform): boolean;
|
||||
checkWorkspaceShellPermissions(osOverride?: platform.OperatingSystem): boolean;
|
||||
}
|
||||
|
||||
export interface ITerminalFont {
|
||||
@@ -268,7 +268,7 @@ export interface ITerminalService {
|
||||
preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise<string>;
|
||||
|
||||
extHostReady(remoteAuthority: string): void;
|
||||
requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void;
|
||||
requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void;
|
||||
}
|
||||
|
||||
export const enum Direction {
|
||||
@@ -714,6 +714,7 @@ export interface ITerminalProcessExtHostRequest {
|
||||
activeWorkspaceRootUri: URI;
|
||||
cols: number;
|
||||
rows: number;
|
||||
isWorkspaceShellAllowed: boolean;
|
||||
}
|
||||
|
||||
export enum LinuxDistro {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati
|
||||
* This module contains utility functions related to the environment, cwd and paths.
|
||||
*/
|
||||
|
||||
export function mergeEnvironments(parent: platform.IProcessEnvironment, other?: ITerminalEnvironment): void {
|
||||
export function mergeEnvironments(parent: platform.IProcessEnvironment, other: ITerminalEnvironment | undefined): void {
|
||||
if (!other) {
|
||||
return;
|
||||
}
|
||||
@@ -49,14 +49,28 @@ function _mergeEnvironmentValue(env: ITerminalEnvironment, key: string, value: s
|
||||
}
|
||||
}
|
||||
|
||||
export function addTerminalEnvironmentKeys(env: ITerminalEnvironment, version: string | undefined, locale: string | undefined, setLocaleVariables: boolean): void {
|
||||
export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, version: string | undefined, locale: string | undefined, setLocaleVariables: boolean): void {
|
||||
env['TERM_PROGRAM'] = 'vscode';
|
||||
env['TERM_PROGRAM_VERSION'] = version ? version : null;
|
||||
if (version) {
|
||||
env['TERM_PROGRAM_VERSION'] = version;
|
||||
}
|
||||
if (setLocaleVariables) {
|
||||
env['LANG'] = _getLangEnvVariable(locale);
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | NodeJS.ProcessEnv | undefined) {
|
||||
if (!other) {
|
||||
return;
|
||||
}
|
||||
for (const key of Object.keys(other)) {
|
||||
const value = other[key];
|
||||
if (value) {
|
||||
env[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: ITerminalEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder | null): ITerminalEnvironment {
|
||||
Object.keys(env).forEach((key) => {
|
||||
const value = env[key];
|
||||
@@ -144,3 +158,34 @@ export function escapeNonWindowsPath(path: string): string {
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
export function mergeDefaultShellPathAndArgs(
|
||||
shell: IShellLaunchConfig,
|
||||
fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined },
|
||||
isWorkspaceShellAllowed: boolean,
|
||||
platformOverride: platform.Platform = platform.platform
|
||||
): void {
|
||||
const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux';
|
||||
const shellConfigValue = fetchSetting(`terminal.integrated.shell.${platformKey}`);
|
||||
// const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`);
|
||||
const shellArgsConfigValue = fetchSetting(`terminal.integrated.shellArgs.${platformKey}`);
|
||||
// const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`);
|
||||
|
||||
shell.executable = (isWorkspaceShellAllowed ? <string>shellConfigValue.value : <string>shellConfigValue.user) || <string>shellConfigValue.default;
|
||||
shell.args = (isWorkspaceShellAllowed ? <string[]>shellArgsConfigValue.value : <string[]>shellArgsConfigValue.user) || <string[]>shellArgsConfigValue.default;
|
||||
|
||||
// Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's
|
||||
// safe to assume that this was used by accident as Sysnative does not
|
||||
// exist and will break the terminal in non-WoW64 environments.
|
||||
if ((platformOverride === platform.Platform.Windows) && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') && process.env.windir) {
|
||||
const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase();
|
||||
if (shell.executable && shell.executable.toLowerCase().indexOf(sysnativePath) === 0) {
|
||||
shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert / to \ on Windows for convenience
|
||||
if (shell.executable && platformOverride === platform.Platform.Windows) {
|
||||
shell.executable = shell.executable.replace(/\//g, '\\');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
let hasReceivedResponse: boolean = false;
|
||||
|
||||
export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerminalProcessExtHostProxy {
|
||||
private _disposables: IDisposable[] = [];
|
||||
@@ -43,10 +47,19 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm
|
||||
activeWorkspaceRootUri: URI,
|
||||
cols: number,
|
||||
rows: number,
|
||||
@ITerminalService private readonly _terminalService: ITerminalService
|
||||
configHelper: ITerminalConfigHelper,
|
||||
@ITerminalService private readonly _terminalService: ITerminalService,
|
||||
@IRemoteAgentService readonly remoteAgentService: IRemoteAgentService
|
||||
) {
|
||||
this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows);
|
||||
setTimeout(() => this._onProcessTitleChanged.fire('Starting...'), 0);
|
||||
remoteAgentService.getEnvironment().then(env => {
|
||||
if (!env) {
|
||||
throw new Error('Could not fetch environment');
|
||||
}
|
||||
this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper.checkWorkspaceShellPermissions(env.os));
|
||||
});
|
||||
if (!hasReceivedResponse) {
|
||||
setTimeout(() => this._onProcessTitleChanged.fire(nls.localize('terminal.integrated.starting', "Starting...")), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -59,6 +72,7 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm
|
||||
}
|
||||
|
||||
public emitTitle(title: string): void {
|
||||
// hasReceivedResponse = true;
|
||||
this._onProcessTitleChanged.fire(title);
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ export abstract class TerminalService implements ITerminalService {
|
||||
return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction);
|
||||
}
|
||||
|
||||
public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void {
|
||||
public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void {
|
||||
this._extensionService.whenInstalledExtensionsRegistered().then(async () => {
|
||||
// Wait for the remoteAuthority to be ready (and listening for events) before proceeding
|
||||
const conn = this._remoteAgentService.getConnection();
|
||||
@@ -127,7 +127,7 @@ export abstract class TerminalService implements ITerminalService {
|
||||
while (!this._extHostsReady[remoteAuthority] && ++retries < 50) {
|
||||
await timeout(100);
|
||||
}
|
||||
this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows });
|
||||
this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, isWorkspaceShellAllowed });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -234,8 +234,8 @@ CommandsRegistry.registerCommand('_workbench.captureSyntaxTokens', function (acc
|
||||
let fileName = basename(resource);
|
||||
let snapper = accessor.get(IInstantiationService).createInstance(Snapper);
|
||||
|
||||
return fileService.resolveContent(resource).then(content => {
|
||||
return snapper.captureSyntaxTokens(fileName, content.value);
|
||||
return fileService.readFile(resource).then(content => {
|
||||
return snapper.captureSyntaxTokens(fileName, content.value.toString());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import * as modes from 'vs/editor/common/modes';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
|
||||
@@ -118,7 +118,7 @@ class WebviewProtocolProvider extends Disposable {
|
||||
private readonly _extensionLocation: URI | undefined,
|
||||
private readonly _getLocalResourceRoots: () => ReadonlyArray<URI>,
|
||||
private readonly _environmentService: IEnvironmentService,
|
||||
private readonly _fileService: IFileService,
|
||||
private readonly _textFileService: ITextFileService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -137,11 +137,11 @@ class WebviewProtocolProvider extends Disposable {
|
||||
|
||||
const appRootUri = URI.file(this._environmentService.appRoot);
|
||||
|
||||
registerFileProtocol(contents, WebviewProtocol.CoreResource, this._fileService, undefined, () => [
|
||||
registerFileProtocol(contents, WebviewProtocol.CoreResource, this._textFileService, undefined, () => [
|
||||
appRootUri
|
||||
]);
|
||||
|
||||
registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._fileService, this._extensionLocation, () =>
|
||||
registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._textFileService, this._extensionLocation, () =>
|
||||
this._getLocalResourceRoots()
|
||||
);
|
||||
}
|
||||
@@ -374,7 +374,7 @@ export class WebviewElement extends Disposable implements Webview {
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IFileService fileService: IFileService,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@ITunnelService tunnelService: ITunnelService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@@ -412,7 +412,7 @@ export class WebviewElement extends Disposable implements Webview {
|
||||
this._options.extension ? this._options.extension.location : undefined,
|
||||
() => (this._contentOptions.localResourceRoots || []),
|
||||
environmentService,
|
||||
fileService));
|
||||
textFileService));
|
||||
|
||||
this._register(new WebviewPortMappingProvider(
|
||||
session,
|
||||
|
||||
@@ -6,16 +6,16 @@ import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime';
|
||||
import { extname, sep } from 'vs/base/common/path';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
export const enum WebviewProtocol {
|
||||
CoreResource = 'vscode-core-resource',
|
||||
VsCodeResource = 'vscode-resource',
|
||||
}
|
||||
|
||||
function resolveContent(fileService: IFileService, resource: URI, mime: string, callback: any): void {
|
||||
fileService.resolveContent(resource, { encoding: 'binary' }).then(contents => {
|
||||
function resolveContent(textFileService: ITextFileService, resource: URI, mime: string, callback: any): void {
|
||||
textFileService.read(resource, { encoding: 'binary' }).then(contents => {
|
||||
callback({
|
||||
data: Buffer.from(contents.value, contents.encoding),
|
||||
mimeType: mime
|
||||
@@ -29,7 +29,7 @@ function resolveContent(fileService: IFileService, resource: URI, mime: string,
|
||||
export function registerFileProtocol(
|
||||
contents: Electron.WebContents,
|
||||
protocol: WebviewProtocol,
|
||||
fileService: IFileService,
|
||||
textFileService: ITextFileService,
|
||||
extensionLocation: URI | undefined,
|
||||
getRoots: () => ReadonlyArray<URI>
|
||||
) {
|
||||
@@ -44,7 +44,7 @@ export function registerFileProtocol(
|
||||
requestResourcePath: requestUri.path
|
||||
})
|
||||
});
|
||||
resolveContent(fileService, redirectedUri, getMimeType(requestUri), callback);
|
||||
resolveContent(textFileService, redirectedUri, getMimeType(requestUri), callback);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function registerFileProtocol(
|
||||
const normalizedPath = URI.file(requestPath);
|
||||
for (const root of getRoots()) {
|
||||
if (startsWith(normalizedPath.fsPath, root.fsPath + sep)) {
|
||||
resolveContent(fileService, normalizedPath, getMimeType(normalizedPath), callback);
|
||||
resolveContent(textFileService, normalizedPath, getMimeType(normalizedPath), callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}) : this.textFileService.resolve(URI.file(resource.fsPath)).then(content => content.value));
|
||||
}) : this.textFileService.readStream(URI.file(resource.fsPath)).then(content => content.value));
|
||||
return content.then(content => {
|
||||
let codeEditorModel = this.modelService.getModel(resource);
|
||||
if (!codeEditorModel) {
|
||||
@@ -61,7 +61,7 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi
|
||||
}
|
||||
|
||||
public provideTextContent(resource: URI): Promise<ITextModel> {
|
||||
return this.textFileService.resolve(URI.file(resource.fsPath)).then(content => {
|
||||
return this.textFileService.readStream(URI.file(resource.fsPath)).then(content => {
|
||||
let codeEditorModel = this.modelService.getModel(resource);
|
||||
if (!codeEditorModel) {
|
||||
const j = parseInt(resource.fragment);
|
||||
|
||||
Reference in New Issue
Block a user