mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 (#14050)
* Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 * Fix breaks * Extension management fixes * Fix breaks in windows bundling * Fix/skip failing tests * Update distro * Add clear to nuget.config * Add hygiene task * Bump distro * Fix hygiene issue * Add build to hygiene exclusion * Update distro * Update hygiene * Hygiene exclusions * Update tsconfig * Bump distro for server breaks * Update build config * Update darwin path * Add done calls to notebook tests * Skip failing tests * Disable smoke tests
This commit is contained in:
@@ -13,7 +13,7 @@ import { IWindowSettings, IWindowOpenable, IOpenWindowOptions, isFolderToOpen, i
|
||||
import { pathsToEditors } from 'vs/workbench/common/editor';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { trackFocus } from 'vs/base/browser/dom';
|
||||
import { IModifierKeyStatus, ModifierKeyEmitter, trackFocus } from 'vs/base/browser/dom';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
@@ -23,6 +23,8 @@ import { parseLineAndColumnAware } from 'vs/base/common/extpath';
|
||||
import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { BeforeShutdownEvent, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
/**
|
||||
* A workspace to open in the workbench can either be:
|
||||
@@ -57,20 +59,41 @@ export interface IWorkspaceProvider {
|
||||
open(workspace: IWorkspace, options?: { reuse?: boolean, payload?: object }): Promise<void>;
|
||||
}
|
||||
|
||||
enum HostShutdownReason {
|
||||
|
||||
/**
|
||||
* An unknown shutdown reason.
|
||||
*/
|
||||
Unknown = 1,
|
||||
|
||||
/**
|
||||
* A shutdown that was potentially triggered by keyboard use.
|
||||
*/
|
||||
Keyboard = 2,
|
||||
|
||||
/**
|
||||
* An explicit shutdown via code.
|
||||
*/
|
||||
Api = 3
|
||||
}
|
||||
|
||||
export class BrowserHostService extends Disposable implements IHostService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private workspaceProvider: IWorkspaceProvider;
|
||||
|
||||
private shutdownReason = HostShutdownReason.Unknown;
|
||||
|
||||
constructor(
|
||||
@ILayoutService private readonly layoutService: ILayoutService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -82,6 +105,51 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
async open() { }
|
||||
};
|
||||
}
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Veto shutdown depending on `window.confirmBeforeClose` setting
|
||||
this._register(this.lifecycleService.onBeforeShutdown(e => this.onBeforeShutdown(e)));
|
||||
|
||||
// Track modifier keys to detect keybinding usage
|
||||
this._register(ModifierKeyEmitter.getInstance().event(e => this.updateShutdownReasonFromEvent(e)));
|
||||
}
|
||||
|
||||
private onBeforeShutdown(e: BeforeShutdownEvent): void {
|
||||
switch (this.shutdownReason) {
|
||||
|
||||
// Unknown / Keyboard shows veto depending on setting
|
||||
case HostShutdownReason.Unknown:
|
||||
case HostShutdownReason.Keyboard:
|
||||
const confirmBeforeClose = this.configurationService.getValue<'always' | 'keyboardOnly' | 'never'>('window.confirmBeforeClose');
|
||||
if (confirmBeforeClose === 'always' || (confirmBeforeClose === 'keyboardOnly' && this.shutdownReason === HostShutdownReason.Keyboard)) {
|
||||
this.logService.warn(`Unload veto: window.confirmBeforeClose=${confirmBeforeClose}`);
|
||||
e.veto(true);
|
||||
}
|
||||
break;
|
||||
|
||||
// Api never shows veto
|
||||
case HostShutdownReason.Api:
|
||||
break;
|
||||
}
|
||||
|
||||
// Unset for next shutdown
|
||||
this.shutdownReason = HostShutdownReason.Unknown;
|
||||
}
|
||||
|
||||
private updateShutdownReasonFromEvent(e: IModifierKeyStatus): void {
|
||||
if (this.shutdownReason === HostShutdownReason.Api) {
|
||||
return; // do not overwrite any explicitly set shutdown reason
|
||||
}
|
||||
|
||||
if (ModifierKeyEmitter.getInstance().isModifierPressed) {
|
||||
this.shutdownReason = HostShutdownReason.Keyboard;
|
||||
} else {
|
||||
this.shutdownReason = HostShutdownReason.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
//#region Focus
|
||||
@@ -137,13 +205,13 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
if (options?.addMode) {
|
||||
foldersToAdd.push(({ uri: openable.folderUri }));
|
||||
} else {
|
||||
this.workspaceProvider.open({ folderUri: openable.folderUri }, { reuse: this.shouldReuse(options, false /* no file */), payload });
|
||||
this.doOpen({ folderUri: openable.folderUri }, { reuse: this.shouldReuse(options, false /* no file */), payload });
|
||||
}
|
||||
}
|
||||
|
||||
// Workspace
|
||||
else if (isWorkspaceToOpen(openable)) {
|
||||
this.workspaceProvider.open({ workspaceUri: openable.workspaceUri }, { reuse: this.shouldReuse(options, false /* no file */), payload });
|
||||
this.doOpen({ workspaceUri: openable.workspaceUri }, { reuse: this.shouldReuse(options, false /* no file */), payload });
|
||||
}
|
||||
|
||||
// File (handled later in bulk)
|
||||
@@ -155,88 +223,92 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
// Handle Folders to Add
|
||||
if (foldersToAdd.length > 0) {
|
||||
this.instantiationService.invokeFunction(accessor => {
|
||||
const workspaceEditingService: IWorkspaceEditingService = accessor.get(IWorkspaceEditingService);
|
||||
const workspaceEditingService: IWorkspaceEditingService = accessor.get(IWorkspaceEditingService); // avoid heavy dependencies (https://github.com/microsoft/vscode/issues/108522)
|
||||
workspaceEditingService.addFolders(foldersToAdd);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle Files
|
||||
if (fileOpenables.length > 0) {
|
||||
this.instantiationService.invokeFunction(async accessor => {
|
||||
const editorService = accessor.get(IEditorService); // avoid heavy dependencies (https://github.com/microsoft/vscode/issues/108522)
|
||||
|
||||
// Support diffMode
|
||||
if (options?.diffMode && fileOpenables.length === 2) {
|
||||
const editors = await pathsToEditors(fileOpenables, this.fileService);
|
||||
if (editors.length !== 2 || !editors[0].resource || !editors[1].resource) {
|
||||
return; // invalid resources
|
||||
}
|
||||
|
||||
// Same Window: open via editor service in current window
|
||||
if (this.shouldReuse(options, true /* file */)) {
|
||||
this.editorService.openEditor({
|
||||
leftResource: editors[0].resource,
|
||||
rightResource: editors[1].resource
|
||||
});
|
||||
}
|
||||
|
||||
// New Window: open into empty window
|
||||
else {
|
||||
const environment = new Map<string, string>();
|
||||
environment.set('diffFileSecondary', editors[0].resource.toString());
|
||||
environment.set('diffFilePrimary', editors[1].resource.toString());
|
||||
|
||||
this.workspaceProvider.open(undefined, { payload: Array.from(environment.entries()) });
|
||||
}
|
||||
}
|
||||
|
||||
// Just open normally
|
||||
else {
|
||||
for (const openable of fileOpenables) {
|
||||
// Support diffMode
|
||||
if (options?.diffMode && fileOpenables.length === 2) {
|
||||
const editors = await pathsToEditors(fileOpenables, this.fileService);
|
||||
if (editors.length !== 2 || !editors[0].resource || !editors[1].resource) {
|
||||
return; // invalid resources
|
||||
}
|
||||
|
||||
// Same Window: open via editor service in current window
|
||||
if (this.shouldReuse(options, true /* file */)) {
|
||||
let openables: IPathData[] = [];
|
||||
|
||||
// Support: --goto parameter to open on line/col
|
||||
if (options?.gotoLineMode) {
|
||||
const pathColumnAware = parseLineAndColumnAware(openable.fileUri.path);
|
||||
openables = [{
|
||||
fileUri: openable.fileUri.with({ path: pathColumnAware.path }),
|
||||
lineNumber: pathColumnAware.line,
|
||||
columnNumber: pathColumnAware.column
|
||||
}];
|
||||
} else {
|
||||
openables = [openable];
|
||||
}
|
||||
|
||||
this.editorService.openEditors(await pathsToEditors(openables, this.fileService));
|
||||
editorService.openEditor({
|
||||
leftResource: editors[0].resource,
|
||||
rightResource: editors[1].resource,
|
||||
options: { pinned: true }
|
||||
});
|
||||
}
|
||||
|
||||
// New Window: open into empty window
|
||||
else {
|
||||
const environment = new Map<string, string>();
|
||||
environment.set('openFile', openable.fileUri.toString());
|
||||
environment.set('diffFileSecondary', editors[0].resource.toString());
|
||||
environment.set('diffFilePrimary', editors[1].resource.toString());
|
||||
|
||||
if (options?.gotoLineMode) {
|
||||
environment.set('gotoLineMode', 'true');
|
||||
}
|
||||
|
||||
this.workspaceProvider.open(undefined, { payload: Array.from(environment.entries()) });
|
||||
this.doOpen(undefined, { payload: Array.from(environment.entries()) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Support wait mode
|
||||
const waitMarkerFileURI = options?.waitMarkerFileURI;
|
||||
if (waitMarkerFileURI) {
|
||||
(async () => {
|
||||
// Just open normally
|
||||
else {
|
||||
for (const openable of fileOpenables) {
|
||||
|
||||
// Wait for the resources to be closed in the editor...
|
||||
await this.editorService.whenClosed(fileOpenables.map(openable => ({ resource: openable.fileUri })), { waitForSaved: true });
|
||||
// Same Window: open via editor service in current window
|
||||
if (this.shouldReuse(options, true /* file */)) {
|
||||
let openables: IPathData[] = [];
|
||||
|
||||
// ...before deleting the wait marker file
|
||||
await this.fileService.del(waitMarkerFileURI);
|
||||
})();
|
||||
}
|
||||
// Support: --goto parameter to open on line/col
|
||||
if (options?.gotoLineMode) {
|
||||
const pathColumnAware = parseLineAndColumnAware(openable.fileUri.path);
|
||||
openables = [{
|
||||
fileUri: openable.fileUri.with({ path: pathColumnAware.path }),
|
||||
lineNumber: pathColumnAware.line,
|
||||
columnNumber: pathColumnAware.column
|
||||
}];
|
||||
} else {
|
||||
openables = [openable];
|
||||
}
|
||||
|
||||
editorService.openEditors(await pathsToEditors(openables, this.fileService));
|
||||
}
|
||||
|
||||
// New Window: open into empty window
|
||||
else {
|
||||
const environment = new Map<string, string>();
|
||||
environment.set('openFile', openable.fileUri.toString());
|
||||
|
||||
if (options?.gotoLineMode) {
|
||||
environment.set('gotoLineMode', 'true');
|
||||
}
|
||||
|
||||
this.doOpen(undefined, { payload: Array.from(environment.entries()) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Support wait mode
|
||||
const waitMarkerFileURI = options?.waitMarkerFileURI;
|
||||
if (waitMarkerFileURI) {
|
||||
(async () => {
|
||||
|
||||
// Wait for the resources to be closed in the editor...
|
||||
await editorService.whenClosed(fileOpenables.map(openable => ({ resource: openable.fileUri })), { waitForSaved: true });
|
||||
|
||||
// ...before deleting the wait marker file
|
||||
await this.fileService.del(waitMarkerFileURI);
|
||||
})();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +350,7 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
return true; // always handle --wait in same window
|
||||
}
|
||||
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
const openInNewWindowConfig = isFile ? (windowConfig?.openFilesInNewWindow || 'off' /* default */) : (windowConfig?.openFoldersInNewWindow || 'default' /* default */);
|
||||
|
||||
let openInNewWindow = (options.preferNewWindow || !!options.forceNewWindow) && !options.forceReuseWindow;
|
||||
@@ -290,7 +362,18 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
}
|
||||
|
||||
private async doOpenEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void> {
|
||||
return this.workspaceProvider.open(undefined, { reuse: options?.forceReuseWindow });
|
||||
return this.doOpen(undefined, { reuse: options?.forceReuseWindow });
|
||||
}
|
||||
|
||||
private doOpen(workspace: IWorkspace, options?: { reuse?: boolean, payload?: object }): Promise<void> {
|
||||
|
||||
// We know that `workspaceProvider.open` will trigger a shutdown
|
||||
// with `options.reuse` so we update `shutdownReason` to reflect that
|
||||
if (options?.reuse) {
|
||||
this.shutdownReason = HostShutdownReason.Api;
|
||||
}
|
||||
|
||||
return this.workspaceProvider.open(workspace, options);
|
||||
}
|
||||
|
||||
async toggleFullScreen(): Promise<void> {
|
||||
@@ -302,13 +385,13 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
try {
|
||||
return await target.requestFullscreen();
|
||||
} catch (error) {
|
||||
console.warn('Toggle Full Screen failed'); // https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen
|
||||
this.logService.warn('toggleFullScreen(): requestFullscreen failed'); // https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return await document.exitFullscreen();
|
||||
} catch (error) {
|
||||
console.warn('Exit Full Screen failed');
|
||||
this.logService.warn('toggleFullScreen(): exitFullscreen failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,7 +405,7 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
(<any>document).webkitExitFullscreen(); // it's async, but doesn't return a real promise.
|
||||
}
|
||||
} catch {
|
||||
console.warn('Enter/Exit Full Screen failed');
|
||||
this.logService.warn('toggleFullScreen(): requestFullscreen/exitFullscreen failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,7 +419,23 @@ export class BrowserHostService extends Disposable implements IHostService {
|
||||
}
|
||||
|
||||
async reload(): Promise<void> {
|
||||
window.location.reload();
|
||||
this.withExpectedShutdown(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
this.withExpectedShutdown(() => {
|
||||
window.close();
|
||||
});
|
||||
}
|
||||
|
||||
private withExpectedShutdown(callback: () => void): void {
|
||||
|
||||
// Update shutdown reason in a way that we do not show a dialog
|
||||
this.shutdownReason = HostShutdownReason.Api;
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
Reference in New Issue
Block a user