mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-20 09:35:38 -05:00
Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)
This commit is contained in:
@@ -40,6 +40,7 @@ import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
|
||||
import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
|
||||
const MAX_URL_LENGTH = platform.isWindows ? 2081 : 5400;
|
||||
|
||||
@@ -79,11 +80,12 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
this.initServices(configuration);
|
||||
|
||||
const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION;
|
||||
this.issueReporterModel = new IssueReporterModel({
|
||||
issueType: configuration.data.issueType || IssueType.Bug,
|
||||
versionInfo: {
|
||||
vscodeVersion: `${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`,
|
||||
os: `${os.type()} ${os.arch()} ${os.release()}`
|
||||
os: `${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}`
|
||||
},
|
||||
extensionsDisabled: !!this.environmentService.disableExtensions,
|
||||
});
|
||||
@@ -104,7 +106,7 @@ export class IssueReporter extends Disposable {
|
||||
this.updatePreviewButtonState();
|
||||
});
|
||||
|
||||
ipcRenderer.on('vscode:issueSystemInfoResponse', (_: unknown, info: any) => {
|
||||
ipcRenderer.on('vscode:issueSystemInfoResponse', (_: unknown, info: SystemInfo) => {
|
||||
this.logService.trace('issueReporter: Received system data');
|
||||
this.issueReporterModel.update({ systemInfo: info });
|
||||
this.receivedSystemInfo = true;
|
||||
@@ -903,19 +905,19 @@ export class IssueReporter extends Disposable {
|
||||
private updateSystemInfo(state: IssueReporterModelData) {
|
||||
const target = document.querySelector('.block-system .block-info');
|
||||
if (target) {
|
||||
let tableHtml = '';
|
||||
Object.keys(state.systemInfo).forEach(k => {
|
||||
const data = typeof state.systemInfo[k] === 'object'
|
||||
? Object.keys(state.systemInfo[k]).map(key => `${key}: ${state.systemInfo[k][key]}`).join('<br>')
|
||||
: state.systemInfo[k];
|
||||
const systemInfo = state.systemInfo!;
|
||||
let renderedData = `
|
||||
<table>
|
||||
<tr><td>CPUs</td><td>${systemInfo.cpus}</td></tr>
|
||||
<tr><td>GPU Status</td><td>${Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('<br>')}</td></tr>
|
||||
<tr><td>Load (avg)</td><td>${systemInfo.load}</td></tr>
|
||||
<tr><td>Memory (System)</td><td>${systemInfo.memory}</td></tr>
|
||||
<tr><td>Process Argv</td><td>${systemInfo.processArgs}</td></tr>
|
||||
<tr><td>Screen Reader</td><td>${systemInfo.screenReader}</td></tr>
|
||||
<tr><td>VM</td><td>${systemInfo.vmHint}</td></tr>
|
||||
</table>`;
|
||||
|
||||
tableHtml += `
|
||||
<tr>
|
||||
<td>${k}</td>
|
||||
<td>${data}</td>
|
||||
</tr>`;
|
||||
});
|
||||
target.innerHTML = `<table>${tableHtml}</table>`;
|
||||
target.innerHTML = renderedData;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue';
|
||||
import { SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
|
||||
export interface IssueReporterData {
|
||||
issueType: IssueType;
|
||||
issueDescription?: string;
|
||||
|
||||
versionInfo?: any;
|
||||
systemInfo?: any;
|
||||
systemInfo?: SystemInfo;
|
||||
processInfo?: any;
|
||||
workspaceInfo?: any;
|
||||
|
||||
@@ -149,13 +150,16 @@ ${this.getInfos()}
|
||||
|---|---|
|
||||
`;
|
||||
|
||||
Object.keys(this._data.systemInfo).forEach(k => {
|
||||
const data = typeof this._data.systemInfo[k] === 'object'
|
||||
? Object.keys(this._data.systemInfo[k]).map(key => `${key}: ${this._data.systemInfo[k][key]}`).join('<br>')
|
||||
: this._data.systemInfo[k];
|
||||
if (this._data.systemInfo) {
|
||||
|
||||
md += `|${k}|${data}|\n`;
|
||||
});
|
||||
md += `|CPUs|${this._data.systemInfo.cpus}|
|
||||
|GPU Status|${Object.keys(this._data.systemInfo.gpuStatus).map(key => `${key}: ${this._data.systemInfo!.gpuStatus[key]}`).join('<br>')}|
|
||||
|Load (avg)|${this._data.systemInfo.load}|
|
||||
|Memory (System)|${this._data.systemInfo.memory}|
|
||||
|Process Argv|${this._data.systemInfo.processArgs}|
|
||||
|Screen Reader|${this._data.systemInfo.screenReader}|
|
||||
|VM|${this._data.systemInfo.vmHint}|`;
|
||||
}
|
||||
|
||||
md += '\n</details>';
|
||||
|
||||
|
||||
@@ -44,7 +44,13 @@ Extensions: none
|
||||
const issueReporterModel = new IssueReporterModel({
|
||||
issueType: 0,
|
||||
systemInfo: {
|
||||
'GPU Status': {
|
||||
os: 'Darwin',
|
||||
cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)',
|
||||
memory: '16.00GB',
|
||||
vmHint: '0%',
|
||||
processArgs: '',
|
||||
screenReader: 'no',
|
||||
gpuStatus: {
|
||||
'2d_canvas': 'enabled',
|
||||
'checker_imaging': 'disabled_off'
|
||||
}
|
||||
@@ -65,8 +71,13 @@ OS version: undefined
|
||||
|
||||
|Item|Value|
|
||||
|---|---|
|
||||
|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)|
|
||||
|GPU Status|2d_canvas: enabled<br>checker_imaging: disabled_off|
|
||||
|
||||
|Load (avg)|undefined|
|
||||
|Memory (System)|16.00GB|
|
||||
|Process Argv||
|
||||
|Screen Reader|no|
|
||||
|VM|0%|
|
||||
</details>Extensions: none
|
||||
<!-- generated by issue reporter -->`);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/processExplorer';
|
||||
import { listProcesses, ProcessItem } from 'vs/base/node/ps';
|
||||
import { listProcesses } from 'vs/base/node/ps';
|
||||
import { webFrame, ipcRenderer, clipboard } from 'electron';
|
||||
import { repeat } from 'vs/base/common/strings';
|
||||
import { totalmem } from 'os';
|
||||
@@ -15,6 +15,7 @@ import * as browser from 'vs/base/browser/browser';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu';
|
||||
import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu';
|
||||
import { ProcessItem } from 'vs/base/common/processes';
|
||||
|
||||
let processList: any[];
|
||||
let mapPidToWindowTitle = new Map<number, string>();
|
||||
|
||||
@@ -20,7 +20,7 @@ export class LogsDataCleaner extends Disposable {
|
||||
}
|
||||
|
||||
private cleanUpOldLogsSoon(): void {
|
||||
let handle: any = setTimeout(() => {
|
||||
let handle: NodeJS.Timeout | undefined = setTimeout(() => {
|
||||
handle = undefined;
|
||||
|
||||
const currentLog = basename(this.environmentService.logsPath);
|
||||
@@ -35,6 +35,11 @@ export class LogsDataCleaner extends Disposable {
|
||||
}).then(null, onUnexpectedError);
|
||||
}, 10 * 1000);
|
||||
|
||||
this._register(toDisposable(() => clearTimeout(handle)));
|
||||
this._register(toDisposable(() => {
|
||||
if (handle) {
|
||||
clearTimeout(handle);
|
||||
handle = undefined;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { basename, dirname, join } from 'vs/base/common/path';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { readdir, rimraf, stat } from 'vs/base/node/pfs';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
@@ -41,13 +41,13 @@ export class NodeCachedDataCleaner {
|
||||
const nodeCachedDataRootDir = dirname(this._environmentService.nodeCachedDataDir);
|
||||
const nodeCachedDataCurrent = basename(this._environmentService.nodeCachedDataDir);
|
||||
|
||||
let handle: any = setTimeout(() => {
|
||||
let handle: NodeJS.Timeout | undefined = setTimeout(() => {
|
||||
handle = undefined;
|
||||
|
||||
readdir(nodeCachedDataRootDir).then(entries => {
|
||||
|
||||
const now = Date.now();
|
||||
const deletes: Promise<any>[] = [];
|
||||
const deletes: Promise<unknown>[] = [];
|
||||
|
||||
entries.forEach(entry => {
|
||||
// name check
|
||||
@@ -76,8 +76,11 @@ export class NodeCachedDataCleaner {
|
||||
|
||||
}, 30 * 1000);
|
||||
|
||||
this._disposables.push({
|
||||
dispose() { clearTimeout(handle); }
|
||||
});
|
||||
this._disposables.push(toDisposable(() => {
|
||||
if (handle) {
|
||||
clearTimeout(handle);
|
||||
handle = undefined;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export class StorageDataCleaner extends Disposable {
|
||||
}
|
||||
|
||||
private cleanUpStorageSoon(): void {
|
||||
let handle: any = setTimeout(() => {
|
||||
let handle: NodeJS.Timeout | undefined = setTimeout(() => {
|
||||
handle = undefined;
|
||||
|
||||
// Leverage the backup workspace file to find out which empty workspace is currently in use to
|
||||
@@ -52,6 +52,11 @@ export class StorageDataCleaner extends Disposable {
|
||||
}).then(null, onUnexpectedError);
|
||||
}, 30 * 1000);
|
||||
|
||||
this._register(toDisposable(() => clearTimeout(handle)));
|
||||
this._register(toDisposable(() => {
|
||||
if (handle) {
|
||||
clearTimeout(handle);
|
||||
handle = undefined;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc';
|
||||
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspacesMainService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { getMachineId } from 'vs/base/node/id';
|
||||
import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32';
|
||||
import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux';
|
||||
@@ -53,7 +53,7 @@ import { IIssueService } from 'vs/platform/issue/common/issue';
|
||||
import { IssueChannel } from 'vs/platform/issue/node/issueIpc';
|
||||
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
|
||||
import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
|
||||
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
|
||||
import { connectRemoteAgentManagement, ManagementPersistentConnection, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
@@ -81,6 +81,8 @@ import { URLService } from 'vs/platform/url/common/urlService';
|
||||
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
|
||||
import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { statSync } from 'fs';
|
||||
|
||||
export class CodeApplication extends Disposable {
|
||||
|
||||
@@ -114,9 +116,9 @@ export class CodeApplication extends Disposable {
|
||||
private registerListeners(): void {
|
||||
|
||||
// We handle uncaught exceptions here to prevent electron from opening a dialog to the user
|
||||
errors.setUnexpectedErrorHandler(err => this.onUnexpectedError(err));
|
||||
setUnexpectedErrorHandler(err => this.onUnexpectedError(err));
|
||||
process.on('uncaughtException', err => this.onUnexpectedError(err));
|
||||
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => errors.onUnexpectedError(reason));
|
||||
process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason));
|
||||
|
||||
// Contextmenu via IPC support
|
||||
registerContextMenuListener();
|
||||
@@ -141,7 +143,7 @@ export class CodeApplication extends Disposable {
|
||||
|
||||
// Security related measures (https://electronjs.org/docs/tutorial/security)
|
||||
// DO NOT CHANGE without consulting the documentation
|
||||
app.on('web-contents-created', (event: any, contents) => {
|
||||
app.on('web-contents-created', (event: Electron.Event, contents) => {
|
||||
contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
|
||||
|
||||
const isValidWebviewSource = (source: string): boolean => {
|
||||
@@ -190,13 +192,13 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
|
||||
let macOpenFileURIs: IURIToOpen[] = [];
|
||||
let runningTimeout: any = null;
|
||||
let runningTimeout: NodeJS.Timeout | null = null;
|
||||
app.on('open-file', (event: Event, path: string) => {
|
||||
this.logService.trace('App#open-file: ', path);
|
||||
event.preventDefault();
|
||||
|
||||
// Keep in array because more might come!
|
||||
macOpenFileURIs.push({ uri: URI.file(path) });
|
||||
macOpenFileURIs.push(getURIToOpenFromPathSync(path));
|
||||
|
||||
// Clear previous handler if any
|
||||
if (runningTimeout !== null) {
|
||||
@@ -245,7 +247,7 @@ export class CodeApplication extends Disposable {
|
||||
});
|
||||
});
|
||||
|
||||
ipc.on('vscode:broadcast', (event: Event, windowId: number, broadcast: { channel: string; payload: any; }) => {
|
||||
ipc.on('vscode:broadcast', (event: Event, windowId: number, broadcast: { channel: string; payload: object; }) => {
|
||||
if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) {
|
||||
this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload);
|
||||
|
||||
@@ -257,6 +259,13 @@ export class CodeApplication extends Disposable {
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('vscode:extensionHostDebug', (_: Event, windowId: number, broadcast: any) => {
|
||||
if (this.windowsMainService) {
|
||||
// Send to all windows (except sender window)
|
||||
this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]);
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('vscode:toggleDevTools', (event: Event) => event.sender.toggleDevTools());
|
||||
ipc.on('vscode:openDevTools', (event: Event) => event.sender.openDevTools());
|
||||
|
||||
@@ -290,7 +299,7 @@ export class CodeApplication extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private onBroadcast(event: string, payload: any): void {
|
||||
private onBroadcast(event: string, payload: object): void {
|
||||
|
||||
// Theme changes
|
||||
if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
|
||||
@@ -571,7 +580,7 @@ export class CodeApplication extends Disposable {
|
||||
this.windowsMainService.ready(this.userEnv);
|
||||
|
||||
// Open our first window
|
||||
const macOpenFiles = (<any>global).macOpenFiles as string[];
|
||||
const macOpenFiles: string[] = (<any>global).macOpenFiles;
|
||||
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
|
||||
const hasCliArgs = hasArgs(args._);
|
||||
const hasFolderURIs = hasArgs(args['folder-uri']);
|
||||
@@ -597,7 +606,7 @@ export class CodeApplication extends Disposable {
|
||||
return this.windowsMainService.open({
|
||||
context: OpenContext.DOCK,
|
||||
cli: args,
|
||||
urisToOpen: macOpenFiles.map(file => ({ uri: URI.file(file) })),
|
||||
urisToOpen: macOpenFiles.map(getURIToOpenFromPathSync),
|
||||
noRecentEntry,
|
||||
waitMarkerFileURI,
|
||||
initialStartup: true
|
||||
@@ -715,7 +724,7 @@ export class CodeApplication extends Disposable {
|
||||
}
|
||||
|
||||
const resolvedAuthorities = new Map<string, ResolvedAuthority>();
|
||||
ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => {
|
||||
ipc.on('vscode:remoteAuthorityResolved', (event: Electron.Event, data: ResolvedAuthority) => {
|
||||
this.logService.info('Received resolved authority', data.authority);
|
||||
resolvedAuthorities.set(data.authority, data);
|
||||
// Make sure to close and remove any existing connections
|
||||
@@ -763,15 +772,28 @@ export class CodeApplication extends Disposable {
|
||||
const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
|
||||
|
||||
// TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider`
|
||||
const fileContents = await channel.call<Uint8Array>('readFile', [uri]);
|
||||
callback(Buffer.from(fileContents));
|
||||
const fileContents = await channel.call<VSBuffer>('readFile', [uri]);
|
||||
callback(<Buffer>fileContents.buffer);
|
||||
} else {
|
||||
callback(undefined);
|
||||
}
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
onUnexpectedError(err);
|
||||
callback(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getURIToOpenFromPathSync(path: string): IURIToOpen {
|
||||
try {
|
||||
const fileStat = statSync(path);
|
||||
if (fileStat.isDirectory()) {
|
||||
return { folderUri: URI.file(path) };
|
||||
} else if (hasWorkspaceFileExtension(path)) {
|
||||
return { workspaceUri: URI.file(path) };
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
return { fileUri: URI.file(path) };
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ function setupIPC(accessor: ServicesAccessor): Promise<Server> {
|
||||
// Show a warning dialog after some timeout if it takes long to talk to the other instance
|
||||
// Skip this if we are running with --wait where it is expected that we wait for a while.
|
||||
// Also skip when gathering diagnostics (--status) which can take a longer time.
|
||||
let startupWarningDialogHandle: any;
|
||||
let startupWarningDialogHandle: NodeJS.Timeout;
|
||||
if (!environmentService.wait && !environmentService.status && !environmentService.args['upload-logs']) {
|
||||
startupWarningDialogHandle = setTimeout(() => {
|
||||
showStartupWarningDialog(
|
||||
@@ -140,12 +140,10 @@ function setupIPC(accessor: ServicesAccessor): Promise<Server> {
|
||||
|
||||
// Process Info
|
||||
if (environmentService.args.status) {
|
||||
return service.getMainProcessInfo().then(info => {
|
||||
return instantiationService.invokeFunction(accessor => {
|
||||
return accessor.get(IDiagnosticsService).getDiagnostics(info).then(diagnostics => {
|
||||
console.log(diagnostics);
|
||||
return Promise.reject(new ExpectedError());
|
||||
});
|
||||
return instantiationService.invokeFunction(accessor => {
|
||||
return accessor.get(IDiagnosticsService).getDiagnostics(service).then(diagnostics => {
|
||||
console.log(diagnostics);
|
||||
return Promise.reject(new ExpectedError());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -316,17 +314,17 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I
|
||||
return new InstantiationService(services, true);
|
||||
}
|
||||
|
||||
function initServices(environmentService: IEnvironmentService, stateService: StateService): Promise<any> {
|
||||
function initServices(environmentService: IEnvironmentService, stateService: StateService): Promise<unknown> {
|
||||
|
||||
// Ensure paths for environment service exist
|
||||
const environmentServiceInitialization = Promise.all<boolean | undefined>([
|
||||
const environmentServiceInitialization = Promise.all<void | undefined>([
|
||||
environmentService.extensionsPath,
|
||||
environmentService.nodeCachedDataDir,
|
||||
environmentService.logsPath,
|
||||
environmentService.globalStorageHome,
|
||||
environmentService.workspaceStorageHome,
|
||||
environmentService.backupHome
|
||||
].map((path): undefined | Promise<boolean> => path ? mkdirp(path) : undefined));
|
||||
].map((path): undefined | Promise<void> => path ? mkdirp(path) : undefined));
|
||||
|
||||
// State service
|
||||
const stateServiceInitialization = stateService.init();
|
||||
|
||||
@@ -26,6 +26,7 @@ import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/node/
|
||||
import { getBackgroundColor } from 'vs/code/electron-main/theme';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { endsWith } from 'vs/base/common/strings';
|
||||
|
||||
export interface IWindowCreationOptions {
|
||||
state: IWindowState;
|
||||
@@ -61,7 +62,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32
|
||||
|
||||
private hiddenTitleBarStyle: boolean;
|
||||
private showTimeoutHandle: any;
|
||||
private showTimeoutHandle: NodeJS.Timeout;
|
||||
private _id: number;
|
||||
private _win: Electron.BrowserWindow;
|
||||
private _lastFocusTime: number;
|
||||
@@ -314,7 +315,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
|
||||
// Inject headers when requests are incoming
|
||||
const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*'];
|
||||
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details: any, cb: any) => {
|
||||
this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => {
|
||||
this.marketplaceHeadersPromise.then(headers => {
|
||||
const requestHeaders = objects.assign(details.requestHeaders, headers);
|
||||
cb({ cancel: false, requestHeaders });
|
||||
@@ -328,7 +329,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
this._win.webContents.session.webRequest.onBeforeRequest(null!, (details, callback) => {
|
||||
if (details.url.indexOf('.svg') > 0) {
|
||||
const uri = URI.parse(details.url);
|
||||
if (uri && !uri.scheme.match(/file/i) && (uri.path as any).endsWith('.svg')) {
|
||||
if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg')) {
|
||||
return callback({ cancel: true });
|
||||
}
|
||||
}
|
||||
@@ -336,8 +337,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
return callback({});
|
||||
});
|
||||
|
||||
this._win.webContents.session.webRequest.onHeadersReceived(null!, (details: any, callback: any) => {
|
||||
const contentType: string[] = (details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']) as any;
|
||||
this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => {
|
||||
const contentType: string[] = (details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']);
|
||||
if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) {
|
||||
return callback({ cancel: true });
|
||||
}
|
||||
@@ -1048,9 +1049,16 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
}
|
||||
}
|
||||
|
||||
let title: string;
|
||||
if (typeof item.title === 'string') {
|
||||
title = item.title;
|
||||
} else {
|
||||
title = item.title.value;
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
label: !icon ? item.title as string : undefined,
|
||||
label: !icon ? title : undefined,
|
||||
icon
|
||||
};
|
||||
});
|
||||
|
||||
@@ -7,33 +7,33 @@ import * as fs from 'fs';
|
||||
import { basename, normalize, join, dirname } from 'vs/base/common/path';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { assign, mixin, equals } from 'vs/base/common/objects';
|
||||
import { assign, mixin } from 'vs/base/common/objects';
|
||||
import { IBackupMainService, IEmptyWindowBackupInfo } from 'vs/platform/backup/common/backup';
|
||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { IStateService } from 'vs/platform/state/common/state';
|
||||
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
|
||||
import { hasArgs, asArray } from 'vs/platform/environment/node/argv';
|
||||
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences } from 'electron';
|
||||
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, FileFilter } from 'electron';
|
||||
import { parseLineAndColumnAware } from 'vs/code/node/paths';
|
||||
import { ILifecycleService, UnloadReason, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions, IURIToOpen, URIType, OpenDialogOptions } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions, IURIToOpen, isFileToOpen, isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/code/node/windowsFinder';
|
||||
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IHistoryMainService, IRecent } from 'vs/platform/history/common/history';
|
||||
import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { normalizeNFC } from 'vs/base/common/normalization';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { exists } from 'vs/base/node/pfs';
|
||||
import { exists, dirExists } from 'vs/base/node/pfs';
|
||||
import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename, originalFSPath, hasTrailingPathSeparator, removeTrailingPathSeparator } from 'vs/base/common/resources';
|
||||
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData } from 'vs/code/electron-main/windowsStateStorage';
|
||||
@@ -87,7 +87,6 @@ interface IOpenBrowserWindowOptions {
|
||||
interface IPathParseOptions {
|
||||
ignoreFileNotFound?: boolean;
|
||||
gotoLineMode?: boolean;
|
||||
forceOpenWorkspaceAsFile?: boolean;
|
||||
remoteAuthority?: string;
|
||||
}
|
||||
|
||||
@@ -206,7 +205,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
this.windowsState.openedWindows = [];
|
||||
}
|
||||
|
||||
this.dialogs = new Dialogs(environmentService, telemetryService, stateService, this);
|
||||
this.dialogs = new Dialogs(stateService, this);
|
||||
this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, this);
|
||||
}
|
||||
|
||||
@@ -219,7 +218,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
private registerListeners(): void {
|
||||
|
||||
// React to workbench ready events from windows
|
||||
ipc.on('vscode:workbenchReady', (event: any, windowId: number) => {
|
||||
ipc.on('vscode:workbenchReady', (event: Electron.Event, windowId: number) => {
|
||||
this.logService.trace('IPC#vscode-workbenchReady');
|
||||
|
||||
const win = this.getWindowById(windowId);
|
||||
@@ -843,26 +842,26 @@ export class WindowsManager implements IWindowsMainService {
|
||||
private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPathToOpen[] {
|
||||
const pathsToOpen: IPathToOpen[] = [];
|
||||
const cli = openConfig.cli;
|
||||
const parseOptions: IPathParseOptions = { gotoLineMode: cli && cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile };
|
||||
const parseOptions: IPathParseOptions = { gotoLineMode: cli && cli.goto };
|
||||
for (const pathToOpen of openConfig.urisToOpen || []) {
|
||||
if (!pathToOpen) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const path = this.parseUri(pathToOpen.uri, pathToOpen.typeHint, parseOptions);
|
||||
const path = this.parseUri(pathToOpen, parseOptions);
|
||||
if (path) {
|
||||
path.label = pathToOpen.label;
|
||||
pathsToOpen.push(path);
|
||||
} else {
|
||||
|
||||
const uri = resourceFromURIToOpen(pathToOpen);
|
||||
// Warn about the invalid URI or path
|
||||
let message, detail;
|
||||
if (pathToOpen.uri.scheme === Schemas.file) {
|
||||
if (uri.scheme === Schemas.file) {
|
||||
message = localize('pathNotExistTitle', "Path does not exist");
|
||||
detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen.uri.fsPath);
|
||||
detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", uri.fsPath);
|
||||
} else {
|
||||
message = localize('uriInvalidTitle', "URI can not be opened");
|
||||
detail = localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", pathToOpen.uri.toString());
|
||||
detail = localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", uri.toString());
|
||||
}
|
||||
const options: Electron.MessageBoxOptions = {
|
||||
title: product.nameLong,
|
||||
@@ -885,19 +884,25 @@ export class WindowsManager implements IWindowsMainService {
|
||||
|
||||
// folder uris
|
||||
const folderUris = asArray(cli['folder-uri']);
|
||||
for (let folderUri of folderUris) {
|
||||
const path = this.parseUri(this.argToUri(folderUri), 'folder', parseOptions);
|
||||
if (path) {
|
||||
pathsToOpen.push(path);
|
||||
for (let f of folderUris) {
|
||||
const folderUri = this.argToUri(f);
|
||||
if (folderUri) {
|
||||
const path = this.parseUri({ folderUri }, parseOptions);
|
||||
if (path) {
|
||||
pathsToOpen.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// file uris
|
||||
const fileUris = asArray(cli['file-uri']);
|
||||
for (let fileUri of fileUris) {
|
||||
const path = this.parseUri(this.argToUri(fileUri), 'file');
|
||||
if (path) {
|
||||
pathsToOpen.push(path);
|
||||
for (let f of fileUris) {
|
||||
const fileUri = this.argToUri(f);
|
||||
if (fileUri) {
|
||||
const path = this.parseUri({ fileUri }, parseOptions);
|
||||
if (path) {
|
||||
pathsToOpen.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -944,12 +949,12 @@ export class WindowsManager implements IWindowsMainService {
|
||||
const windowsToOpen: IPathToOpen[] = [];
|
||||
for (const openedWindow of openedWindows) {
|
||||
if (openedWindow.workspace) { // Workspaces
|
||||
const pathToOpen = this.parseUri(openedWindow.workspace.configPath, 'file', { remoteAuthority: openedWindow.remoteAuthority });
|
||||
const pathToOpen = this.parseUri({ workspaceUri: openedWindow.workspace.configPath }, { remoteAuthority: openedWindow.remoteAuthority });
|
||||
if (pathToOpen && pathToOpen.workspace) {
|
||||
windowsToOpen.push(pathToOpen);
|
||||
}
|
||||
} else if (openedWindow.folderUri) { // Folders
|
||||
const pathToOpen = this.parseUri(openedWindow.folderUri, 'folder', { remoteAuthority: openedWindow.remoteAuthority });
|
||||
const pathToOpen = this.parseUri({ folderUri: openedWindow.folderUri }, { remoteAuthority: openedWindow.remoteAuthority });
|
||||
if (pathToOpen && pathToOpen.folderUri) {
|
||||
windowsToOpen.push(pathToOpen);
|
||||
}
|
||||
@@ -975,7 +980,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
restoreWindows = 'all'; // always reopen all windows when an update was applied
|
||||
} else {
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
restoreWindows = ((windowConfig && windowConfig.restoreWindows) || 'one') as RestoreWindowsSetting;
|
||||
restoreWindows = ((windowConfig && windowConfig.restoreWindows) || 'one');
|
||||
|
||||
if (['all', 'folders', 'one', 'none'].indexOf(restoreWindows) === -1) {
|
||||
restoreWindows = 'one';
|
||||
@@ -999,12 +1004,13 @@ export class WindowsManager implements IWindowsMainService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private parseUri(uri: URI | undefined, typeHint?: URIType, options: IPathParseOptions = {}): IPathToOpen | undefined {
|
||||
if (!uri || !uri.scheme) {
|
||||
private parseUri(uriToOpen: IURIToOpen, options: IPathParseOptions = {}): IPathToOpen | undefined {
|
||||
if (!uriToOpen) {
|
||||
return undefined;
|
||||
}
|
||||
let uri = resourceFromURIToOpen(uriToOpen);
|
||||
if (uri.scheme === Schemas.file) {
|
||||
return this.parsePath(uri.fsPath, options);
|
||||
return this.parsePath(uri.fsPath, options, isFileToOpen(uriToOpen));
|
||||
}
|
||||
|
||||
// open remote if either specified in the cli or if it's a remotehost URI
|
||||
@@ -1013,21 +1019,12 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// normalize URI
|
||||
uri = normalizePath(uri);
|
||||
|
||||
|
||||
// remove trailing slash
|
||||
if (hasTrailingPathSeparator(uri)) {
|
||||
uri = removeTrailingPathSeparator(uri);
|
||||
if (!typeHint) {
|
||||
typeHint = 'folder';
|
||||
}
|
||||
}
|
||||
|
||||
// if there's no type hint
|
||||
if (!typeHint && (hasWorkspaceFileExtension(uri.path) || options.gotoLineMode)) {
|
||||
typeHint = 'file';
|
||||
}
|
||||
|
||||
if (typeHint === 'file') {
|
||||
if (isFileToOpen(uriToOpen)) {
|
||||
if (options.gotoLineMode) {
|
||||
const parsedPath = parseLineAndColumnAware(uri.path);
|
||||
return {
|
||||
@@ -1037,16 +1034,15 @@ export class WindowsManager implements IWindowsMainService {
|
||||
remoteAuthority
|
||||
};
|
||||
}
|
||||
if (hasWorkspaceFileExtension(uri.path) && !options.forceOpenWorkspaceAsFile) {
|
||||
return {
|
||||
workspace: getWorkspaceIdentifier(uri),
|
||||
remoteAuthority
|
||||
};
|
||||
}
|
||||
return {
|
||||
fileUri: uri,
|
||||
remoteAuthority
|
||||
};
|
||||
} else if (isWorkspaceToOpen(uriToOpen)) {
|
||||
return {
|
||||
workspace: getWorkspaceIdentifier(uri),
|
||||
remoteAuthority
|
||||
};
|
||||
}
|
||||
return {
|
||||
folderUri: uri,
|
||||
@@ -1054,7 +1050,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
};
|
||||
}
|
||||
|
||||
private parsePath(anyPath: string, options: IPathParseOptions): IPathToOpen | undefined {
|
||||
private parsePath(anyPath: string, options: IPathParseOptions, forceOpenWorkspaceAsFile?: boolean): IPathToOpen | undefined {
|
||||
if (!anyPath) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -1079,7 +1075,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
if (candidateStat.isFile()) {
|
||||
|
||||
// Workspace (unless disabled via flag)
|
||||
if (!options.forceOpenWorkspaceAsFile) {
|
||||
if (!forceOpenWorkspaceAsFile) {
|
||||
const workspace = this.workspacesMainService.resolveLocalWorkspaceSync(URI.file(candidate));
|
||||
if (workspace) {
|
||||
return { workspace: { id: workspace.id, configPath: workspace.configPath }, remoteAuthority: workspace.remoteAuthority };
|
||||
@@ -1452,7 +1448,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
// Compute x/y based on display bounds
|
||||
// Note: important to use Math.round() because Electron does not seem to be too happy about
|
||||
// display coordinates that are not absolute numbers.
|
||||
let state = defaultWindowState() as INewWindowState;
|
||||
let state = defaultWindowState();
|
||||
state.x = Math.round(displayToUse.bounds.x + (displayToUse.bounds.width / 2) - (state.width! / 2));
|
||||
state.y = Math.round(displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height! / 2));
|
||||
|
||||
@@ -1482,7 +1478,7 @@ export class WindowsManager implements IWindowsMainService {
|
||||
state = this.ensureNoOverlap(state);
|
||||
}
|
||||
|
||||
state.hasDefaultState = true; // flag as default state
|
||||
(state as INewWindowState).hasDefaultState = true; // flag as default state
|
||||
|
||||
return state;
|
||||
}
|
||||
@@ -1537,10 +1533,6 @@ export class WindowsManager implements IWindowsMainService {
|
||||
return result;
|
||||
}
|
||||
|
||||
pickWorkspaceAndOpen(options: INativeOpenDialogOptions): void {
|
||||
this.workspacesManager.pickWorkspaceAndOpen(options);
|
||||
}
|
||||
|
||||
focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow {
|
||||
const lastActive = this.getLastActiveWindow();
|
||||
if (lastActive) {
|
||||
@@ -1717,49 +1709,83 @@ export class WindowsManager implements IWindowsMainService {
|
||||
this._onWindowClose.fire(win.id);
|
||||
}
|
||||
|
||||
pickFileFolderAndOpen(options: INativeOpenDialogOptions): void {
|
||||
this.doPickAndOpen(options, true /* pick folders */, true /* pick files */);
|
||||
async pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
const title = localize('open', "Open");
|
||||
const paths = await this.dialogs.pick({ ...options, pickFolders: true, pickFiles: true, title });
|
||||
if (paths) {
|
||||
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFileFolder', options.telemetryExtraData);
|
||||
const urisToOpen = await Promise.all(paths.map(path => {
|
||||
return dirExists(path).then(isDir => isDir ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) });
|
||||
}));
|
||||
this.open({
|
||||
context: OpenContext.DIALOG,
|
||||
contextWindowId: options.windowId,
|
||||
cli: this.environmentService.args,
|
||||
urisToOpen,
|
||||
forceNewWindow: options.forceNewWindow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pickFolderAndOpen(options: INativeOpenDialogOptions): void {
|
||||
this.doPickAndOpen(options, true /* pick folders */, false /* pick files */);
|
||||
async pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
const title = localize('openFolder', "Open Folder");
|
||||
const paths = await this.dialogs.pick({ ...options, pickFolders: true, title });
|
||||
if (paths) {
|
||||
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFolder', options.telemetryExtraData);
|
||||
this.open({
|
||||
context: OpenContext.DIALOG,
|
||||
contextWindowId: options.windowId,
|
||||
cli: this.environmentService.args,
|
||||
urisToOpen: paths.map(path => ({ folderUri: URI.file(path) })),
|
||||
forceNewWindow: options.forceNewWindow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pickFileAndOpen(options: INativeOpenDialogOptions): void {
|
||||
this.doPickAndOpen(options, false /* pick folders */, true /* pick files */);
|
||||
async pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
const title = localize('openFile', "Open File");
|
||||
const paths = await this.dialogs.pick({ ...options, pickFiles: true, title });
|
||||
if (paths) {
|
||||
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFile', options.telemetryExtraData);
|
||||
this.open({
|
||||
context: OpenContext.DIALOG,
|
||||
contextWindowId: options.windowId,
|
||||
cli: this.environmentService.args,
|
||||
urisToOpen: paths.map(path => ({ fileUri: URI.file(path) })),
|
||||
forceNewWindow: options.forceNewWindow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private doPickAndOpen(options: INativeOpenDialogOptions, pickFolders: boolean, pickFiles: boolean): void {
|
||||
const internalOptions = options as IInternalNativeOpenDialogOptions;
|
||||
|
||||
internalOptions.pickFolders = pickFolders;
|
||||
internalOptions.pickFiles = pickFiles;
|
||||
|
||||
const dialogOptions: OpenDialogOptions = internalOptions.dialogOptions || Object.create(null);
|
||||
internalOptions.dialogOptions = dialogOptions;
|
||||
|
||||
if (!internalOptions.dialogOptions.title) {
|
||||
if (pickFolders && pickFiles) {
|
||||
internalOptions.dialogOptions.title = localize('open', "Open");
|
||||
} else if (pickFolders) {
|
||||
internalOptions.dialogOptions.title = localize('openFolder', "Open Folder");
|
||||
} else {
|
||||
internalOptions.dialogOptions.title = localize('openFile', "Open File");
|
||||
}
|
||||
async pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
const title = localize('openWorkspaceTitle', "Open Workspace");
|
||||
const buttonLabel = mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open"));
|
||||
const filters = WORKSPACE_FILTER;
|
||||
const paths = await this.dialogs.pick({ ...options, pickFiles: true, title, filters, buttonLabel });
|
||||
if (paths) {
|
||||
this.sendPickerTelemetry(paths, options.telemetryEventName || 'openWorkspace', options.telemetryExtraData);
|
||||
this.open({
|
||||
context: OpenContext.DIALOG,
|
||||
contextWindowId: options.windowId,
|
||||
cli: this.environmentService.args,
|
||||
urisToOpen: paths.map(path => ({ workspaceUri: URI.file(path) })),
|
||||
forceNewWindow: options.forceNewWindow
|
||||
});
|
||||
}
|
||||
|
||||
if (!internalOptions.telemetryEventName) {
|
||||
if (pickFolders && pickFiles) {
|
||||
// __GDPR__TODO__ classify event
|
||||
internalOptions.telemetryEventName = 'openFileFolder';
|
||||
} else if (pickFolders) {
|
||||
internalOptions.telemetryEventName = 'openFolder';
|
||||
} else {
|
||||
internalOptions.telemetryEventName = 'openFile';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.dialogs.pickAndOpen(internalOptions);
|
||||
private sendPickerTelemetry(paths: string[], telemetryEventName: string, telemetryExtraData?: ITelemetryData) {
|
||||
|
||||
const numberOfPaths = paths ? paths.length : 0;
|
||||
|
||||
// Telemetry
|
||||
// __GDPR__TODO__ Dynamic event names and dynamic properties. Can not be registered statically.
|
||||
this.telemetryService.publicLog(telemetryEventName, {
|
||||
...telemetryExtraData,
|
||||
outcome: numberOfPaths ? 'success' : 'canceled',
|
||||
numberOfPaths
|
||||
});
|
||||
}
|
||||
|
||||
showMessageBox(options: Electron.MessageBoxOptions, win?: ICodeWindow): Promise<IMessageBoxResult> {
|
||||
@@ -1793,65 +1819,42 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
|
||||
interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions {
|
||||
|
||||
pickFolders?: boolean;
|
||||
pickFiles?: boolean;
|
||||
|
||||
title: string;
|
||||
buttonLabel?: string;
|
||||
filters?: FileFilter[];
|
||||
}
|
||||
|
||||
class Dialogs {
|
||||
|
||||
private static readonly workingDirPickerStorageKey = 'pickerWorkingDir';
|
||||
|
||||
private readonly mapWindowToDialogQueue: Map<number, Queue<any>>;
|
||||
private readonly noWindowDialogQueue: Queue<any>;
|
||||
private readonly mapWindowToDialogQueue: Map<number, Queue<void>>;
|
||||
private readonly noWindowDialogQueue: Queue<void>;
|
||||
|
||||
constructor(
|
||||
private readonly environmentService: IEnvironmentService,
|
||||
private readonly telemetryService: ITelemetryService,
|
||||
private readonly stateService: IStateService,
|
||||
private readonly windowsMainService: IWindowsMainService,
|
||||
private readonly windowsMainService: IWindowsMainService
|
||||
) {
|
||||
this.mapWindowToDialogQueue = new Map<number, Queue<any>>();
|
||||
this.noWindowDialogQueue = new Queue<any>();
|
||||
this.mapWindowToDialogQueue = new Map<number, Queue<void>>();
|
||||
this.noWindowDialogQueue = new Queue<void>();
|
||||
}
|
||||
|
||||
pickAndOpen(options: INativeOpenDialogOptions): void {
|
||||
this.getFileOrFolderUris(options).then(paths => {
|
||||
const numberOfPaths = paths ? paths.length : 0;
|
||||
|
||||
// Telemetry
|
||||
if (options.telemetryEventName) {
|
||||
// __GDPR__TODO__ Dynamic event names and dynamic properties. Can not be registered statically.
|
||||
this.telemetryService.publicLog(options.telemetryEventName, {
|
||||
...options.telemetryExtraData,
|
||||
outcome: numberOfPaths ? 'success' : 'canceled',
|
||||
numberOfPaths
|
||||
});
|
||||
}
|
||||
|
||||
// Open
|
||||
if (numberOfPaths) {
|
||||
this.windowsMainService.open({
|
||||
context: OpenContext.DIALOG,
|
||||
contextWindowId: options.windowId,
|
||||
cli: this.environmentService.args,
|
||||
urisToOpen: paths,
|
||||
forceNewWindow: options.forceNewWindow,
|
||||
forceOpenWorkspaceAsFile: options.dialogOptions && !equals(options.dialogOptions.filters, WORKSPACE_FILTER)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getFileOrFolderUris(options: IInternalNativeOpenDialogOptions): Promise<IURIToOpen[] | undefined> {
|
||||
pick(options: IInternalNativeOpenDialogOptions): Promise<string[] | undefined> {
|
||||
|
||||
// Ensure dialog options
|
||||
const dialogOptions = options.dialogOptions || Object.create(null);
|
||||
options.dialogOptions = dialogOptions;
|
||||
const dialogOptions: Electron.OpenDialogOptions = {
|
||||
title: options.title,
|
||||
buttonLabel: options.buttonLabel,
|
||||
filters: options.filters
|
||||
};
|
||||
|
||||
// Ensure defaultPath
|
||||
if (!dialogOptions.defaultPath) {
|
||||
dialogOptions.defaultPath = this.stateService.getItem<string>(Dialogs.workingDirPickerStorageKey);
|
||||
}
|
||||
dialogOptions.defaultPath = options.defaultPath || this.stateService.getItem<string>(Dialogs.workingDirPickerStorageKey);
|
||||
|
||||
|
||||
// Ensure properties
|
||||
if (typeof options.pickFiles === 'boolean' || typeof options.pickFolders === 'boolean') {
|
||||
@@ -1878,13 +1881,7 @@ class Dialogs {
|
||||
|
||||
// Remember path in storage for next time
|
||||
this.stateService.setItem(Dialogs.workingDirPickerStorageKey, dirname(paths[0]));
|
||||
|
||||
const result: IURIToOpen[] = [];
|
||||
for (const path of paths) {
|
||||
result.push({ uri: URI.file(path) });
|
||||
}
|
||||
|
||||
return result;
|
||||
return paths;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -2057,21 +2054,13 @@ class WorkspacesManager {
|
||||
return { workspace, backupPath };
|
||||
}
|
||||
|
||||
pickWorkspaceAndOpen(options: INativeOpenDialogOptions): void {
|
||||
const window = (typeof options.windowId === 'number' ? this.windowsMainService.getWindowById(options.windowId) : undefined) || this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow();
|
||||
|
||||
this.windowsMainService.pickFileAndOpen({
|
||||
windowId: window ? window.id : undefined,
|
||||
dialogOptions: {
|
||||
buttonLabel: mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")),
|
||||
title: localize('openWorkspaceTitle', "Open Workspace"),
|
||||
filters: WORKSPACE_FILTER,
|
||||
properties: ['openFile'],
|
||||
defaultPath: options.dialogOptions && options.dialogOptions.defaultPath
|
||||
},
|
||||
forceNewWindow: options.forceNewWindow,
|
||||
telemetryEventName: options.telemetryEventName,
|
||||
telemetryExtraData: options.telemetryExtraData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function resourceFromURIToOpen(u: IURIToOpen) {
|
||||
if (isWorkspaceToOpen(u)) {
|
||||
return u.workspaceUri;
|
||||
} else if (isFolderToOpen(u)) {
|
||||
return u.folderUri;
|
||||
}
|
||||
return u.fileUri;
|
||||
}
|
||||
@@ -13,11 +13,10 @@ import pkg from 'vs/platform/product/node/package';
|
||||
import * as paths from 'vs/base/common/path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import { whenDeleted } from 'vs/base/node/pfs';
|
||||
import { whenDeleted, writeFileSync } from 'vs/base/node/pfs';
|
||||
import { findFreePort, randomPort } from 'vs/base/node/ports';
|
||||
import { resolveTerminalEncoding } from 'vs/base/node/encoding';
|
||||
import * as iconv from 'iconv-lite';
|
||||
import { writeFileAndFlushSync } from 'vs/base/node/extfs';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { ProfilingSession, Target } from 'v8-inspect-profiler';
|
||||
|
||||
@@ -98,9 +97,9 @@ export async function main(argv: string[]): Promise<any> {
|
||||
// prevent removing alternate data streams
|
||||
// (see https://github.com/Microsoft/vscode/issues/6363)
|
||||
fs.truncateSync(target, 0);
|
||||
writeFileAndFlushSync(target, data, { flag: 'r+' });
|
||||
writeFileSync(target, data, { flag: 'r+' });
|
||||
} else {
|
||||
writeFileAndFlushSync(target, data);
|
||||
writeFileSync(target, data);
|
||||
}
|
||||
|
||||
// Restore previous mode as needed
|
||||
|
||||
@@ -76,7 +76,7 @@ export class Main {
|
||||
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService
|
||||
) { }
|
||||
|
||||
async run(argv: ParsedArgs): Promise<any> {
|
||||
async run(argv: ParsedArgs): Promise<void> {
|
||||
if (argv['install-source']) {
|
||||
await this.setInstallSource(argv['install-source']);
|
||||
|
||||
@@ -95,11 +95,11 @@ export class Main {
|
||||
}
|
||||
}
|
||||
|
||||
private setInstallSource(installSource: string): Promise<any> {
|
||||
private setInstallSource(installSource: string): Promise<void> {
|
||||
return writeFile(this.environmentService.installSourcePath, installSource.slice(0, 30));
|
||||
}
|
||||
|
||||
private async listExtensions(showVersions: boolean): Promise<any> {
|
||||
private async listExtensions(showVersions: boolean): Promise<void> {
|
||||
const extensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
|
||||
extensions.forEach(e => console.log(getId(e.manifest, showVersions)));
|
||||
}
|
||||
@@ -227,7 +227,7 @@ export class Main {
|
||||
}
|
||||
}
|
||||
|
||||
private async uninstallExtension(extensions: string[]): Promise<any> {
|
||||
private async uninstallExtension(extensions: string[]): Promise<void> {
|
||||
async function getExtensionId(extensionDescription: string): Promise<string> {
|
||||
if (!/\.vsix$/i.test(extensionDescription)) {
|
||||
return extensionDescription;
|
||||
|
||||
@@ -10,7 +10,6 @@ import * as extpath from 'vs/base/common/extpath';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { sanitizeFilePath } from 'vs/base/node/extfs';
|
||||
|
||||
export function validatePaths(args: ParsedArgs): ParsedArgs {
|
||||
|
||||
@@ -45,7 +44,7 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] {
|
||||
pathCandidate = preparePath(cwd, pathCandidate);
|
||||
}
|
||||
|
||||
const sanitizedFilePath = sanitizeFilePath(pathCandidate, cwd);
|
||||
const sanitizedFilePath = extpath.sanitizeFilePath(pathCandidate, cwd);
|
||||
|
||||
const basename = path.basename(sanitizedFilePath);
|
||||
if (basename /* can be empty if code is opened on root */ && !extpath.isValidBasename(basename)) {
|
||||
|
||||
@@ -29,9 +29,9 @@ function getUnixShellEnvironment(): Promise<typeof process.env> {
|
||||
|
||||
const buffers: Buffer[] = [];
|
||||
child.on('error', () => resolve({}));
|
||||
child.stdout.on('data', b => buffers.push(b as Buffer));
|
||||
child.stdout.on('data', b => buffers.push(b));
|
||||
|
||||
child.on('close', (code: number, signal: any) => {
|
||||
child.on('close', code => {
|
||||
if (code !== 0) {
|
||||
return reject(new Error('Failed to get environment'));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user