Merge branch 'master' into sandy081/settingsSync/align
This commit is contained in:
parent
62cbf21904
commit
f57bdeab80
|
@ -195,7 +195,7 @@
|
|||
"underscore": "^1.8.2",
|
||||
"vinyl": "^2.0.0",
|
||||
"vinyl-fs": "^3.0.0",
|
||||
"vscode-debugprotocol": "1.44.0",
|
||||
"vscode-debugprotocol": "1.45.0-pre.0",
|
||||
"vscode-nls-dev": "^3.3.1",
|
||||
"vscode-telemetry-extractor": "^1.6.0",
|
||||
"webpack": "^4.43.0",
|
||||
|
|
|
@ -163,8 +163,11 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
|
|||
this.actionRunner.run(this._action, context);
|
||||
}
|
||||
|
||||
// Only set the tabIndex on the element once it is about to get focused
|
||||
// That way this element wont be a tab stop when it is not needed #106441
|
||||
focus(): void {
|
||||
if (this.element) {
|
||||
this.element.tabIndex = 0;
|
||||
this.element.focus();
|
||||
this.element.classList.add('focused');
|
||||
}
|
||||
|
@ -173,10 +176,21 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
|
|||
blur(): void {
|
||||
if (this.element) {
|
||||
this.element.blur();
|
||||
this.element.tabIndex = -1;
|
||||
this.element.classList.remove('focused');
|
||||
}
|
||||
}
|
||||
|
||||
setFocusable(): void {
|
||||
if (this.element) {
|
||||
this.element.tabIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
get trapsArrowNavigation(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected updateEnabled(): void {
|
||||
// implement in subclass
|
||||
}
|
||||
|
@ -259,14 +273,27 @@ export class ActionViewItem extends BaseActionViewItem {
|
|||
this.updateChecked();
|
||||
}
|
||||
|
||||
// Only set the tabIndex on the element once it is about to get focused
|
||||
// That way this element wont be a tab stop when it is not needed #106441
|
||||
focus(): void {
|
||||
super.focus();
|
||||
|
||||
if (this.label) {
|
||||
this.label.tabIndex = 0;
|
||||
this.label.focus();
|
||||
}
|
||||
}
|
||||
|
||||
blur(): void {
|
||||
if (this.label) {
|
||||
this.label.tabIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
setFocusable(): void {
|
||||
if (this.label) {
|
||||
this.label.tabIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateLabel(): void {
|
||||
if (this.options.label && this.label) {
|
||||
this.label.textContent = this.getAction().label;
|
||||
|
@ -320,7 +347,6 @@ export class ActionViewItem extends BaseActionViewItem {
|
|||
if (this.label) {
|
||||
this.label.removeAttribute('aria-disabled');
|
||||
this.label.classList.remove('disabled');
|
||||
this.label.tabIndex = 0;
|
||||
}
|
||||
|
||||
if (this.element) {
|
||||
|
|
|
@ -140,6 +140,7 @@ export class ActionBar extends Disposable implements IActionRunner {
|
|||
this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
let eventHandled = true;
|
||||
const focusedItem = typeof this.focusedItem === 'number' ? this.viewItems[this.focusedItem] : undefined;
|
||||
|
||||
if (previousKeys && (event.equals(previousKeys[0]) || event.equals(previousKeys[1]))) {
|
||||
eventHandled = this.focusPrevious();
|
||||
|
@ -147,6 +148,12 @@ export class ActionBar extends Disposable implements IActionRunner {
|
|||
eventHandled = this.focusNext();
|
||||
} else if (event.equals(KeyCode.Escape) && this.cancelHasListener) {
|
||||
this._onDidCancel.fire();
|
||||
} else if ((event.equals(KeyCode.Tab) || event.equals(KeyMod.Shift | KeyCode.Tab)) && focusedItem instanceof BaseActionViewItem && focusedItem.trapsArrowNavigation) {
|
||||
if (event.equals(KeyCode.Tab)) {
|
||||
this.focusNext();
|
||||
} else {
|
||||
this.focusPrevious();
|
||||
}
|
||||
} else if (this.isTriggerKeyEvent(event)) {
|
||||
// Staying out of the else branch even if not triggered
|
||||
if (this._triggerKeys.keyDown) {
|
||||
|
@ -294,6 +301,11 @@ export class ActionBar extends Disposable implements IActionRunner {
|
|||
item.setActionContext(this.context);
|
||||
item.render(actionViewItemElement);
|
||||
|
||||
if (this.viewItems.every(i => !i.isEnabled()) && item instanceof BaseActionViewItem && item.isEnabled()) {
|
||||
// We need to allow for the first enabled item to be focused on using tab navigation #106441
|
||||
item.setFocusable();
|
||||
}
|
||||
|
||||
if (index === null || index < 0 || index >= this.actionsList.children.length) {
|
||||
this.actionsList.appendChild(actionViewItemElement);
|
||||
this.viewItems.push(item);
|
||||
|
|
|
@ -78,7 +78,6 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
|
|||
|
||||
this.element.classList.add(...classNames);
|
||||
|
||||
this.element.tabIndex = 0;
|
||||
this.element.setAttribute('role', 'button');
|
||||
this.element.setAttribute('aria-haspopup', 'true');
|
||||
this.element.setAttribute('aria-expanded', 'false');
|
||||
|
|
|
@ -3,19 +3,10 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export const WORKSPACE_TRUST_ENABLED = 'workspace.trustEnabled';
|
||||
export const WORKSPACE_TRUST_URI = URI.parse('workspaceTrust:/Trusted Workspaces');
|
||||
|
||||
export enum WorkspaceTrustScope {
|
||||
Local = 0,
|
||||
|
@ -40,11 +31,6 @@ export function workspaceTrustStateToString(trustState: WorkspaceTrustState) {
|
|||
}
|
||||
}
|
||||
|
||||
export const WorkspaceTrustContext = {
|
||||
PendingRequest: new RawContextKey<boolean>('workspaceTrustPendingRequest', false),
|
||||
TrustState: new RawContextKey<WorkspaceTrustState>('workspaceTrustState', WorkspaceTrustState.Unknown)
|
||||
};
|
||||
|
||||
export interface IWorkspaceTrustModel {
|
||||
|
||||
readonly onDidChangeTrustState: Event<void>;
|
||||
|
@ -86,290 +72,11 @@ export interface IWorkspaceTrustService {
|
|||
getWorkspaceTrustState(): WorkspaceTrustState;
|
||||
isWorkspaceTrustEnabled(): boolean;
|
||||
requireWorkspaceTrust(request: IWorkspaceTrustRequest): Promise<WorkspaceTrustState>;
|
||||
resetWorkspaceTrust(): Promise<WorkspaceTrustState>;
|
||||
}
|
||||
|
||||
interface IWorkspaceTrustStateInfo {
|
||||
export interface IWorkspaceTrustStateInfo {
|
||||
localFolders: { uri: string, trustState: WorkspaceTrustState }[]
|
||||
|
||||
// Removing complexity of remote items
|
||||
//trustedRemoteItems: { uri: string }[]
|
||||
}
|
||||
|
||||
export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key';
|
||||
|
||||
export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustModel {
|
||||
|
||||
private storageKey = WORKSPACE_TRUST_STORAGE_KEY;
|
||||
private trustStateInfo: IWorkspaceTrustStateInfo;
|
||||
|
||||
private readonly _onDidChangeTrustState = this._register(new Emitter<void>());
|
||||
readonly onDidChangeTrustState = this._onDidChangeTrustState.event;
|
||||
|
||||
constructor(
|
||||
private readonly storageService: IStorageService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.trustStateInfo = this.loadTrustInfo();
|
||||
this._register(this.storageService.onDidChangeValue(changeEvent => {
|
||||
if (changeEvent.key === this.storageKey) {
|
||||
this.onDidStorageChange();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private loadTrustInfo(): IWorkspaceTrustStateInfo {
|
||||
const infoAsString = this.storageService.get(this.storageKey, StorageScope.GLOBAL);
|
||||
|
||||
let result: IWorkspaceTrustStateInfo | undefined;
|
||||
try {
|
||||
if (infoAsString) {
|
||||
result = JSON.parse(infoAsString);
|
||||
}
|
||||
} catch { }
|
||||
|
||||
if (!result) {
|
||||
result = {
|
||||
localFolders: [],
|
||||
//trustedRemoteItems: []
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.localFolders) {
|
||||
result.localFolders = [];
|
||||
}
|
||||
|
||||
// if (!result.trustedRemoteItems) {
|
||||
// result.trustedRemoteItems = [];
|
||||
// }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private saveTrustInfo(): void {
|
||||
this.storageService.store(this.storageKey, JSON.stringify(this.trustStateInfo), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
private onDidStorageChange(): void {
|
||||
this.trustStateInfo = this.loadTrustInfo();
|
||||
|
||||
this._onDidChangeTrustState.fire();
|
||||
}
|
||||
|
||||
setFolderTrustState(folder: URI, trustState: WorkspaceTrustState): void {
|
||||
let changed = false;
|
||||
|
||||
if (trustState === WorkspaceTrustState.Unknown) {
|
||||
const before = this.trustStateInfo.localFolders.length;
|
||||
this.trustStateInfo.localFolders = this.trustStateInfo.localFolders.filter(info => info.uri !== folder.toString());
|
||||
|
||||
if (this.trustStateInfo.localFolders.length !== before) {
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
let found = false;
|
||||
for (const trustInfo of this.trustStateInfo.localFolders) {
|
||||
if (trustInfo.uri === folder.toString()) {
|
||||
found = true;
|
||||
if (trustInfo.trustState !== trustState) {
|
||||
trustInfo.trustState = trustState;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
this.trustStateInfo.localFolders.push({ uri: folder.toString(), trustState });
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.saveTrustInfo();
|
||||
}
|
||||
}
|
||||
|
||||
getFolderTrustState(folder: URI): WorkspaceTrustState {
|
||||
for (const trustInfo of this.trustStateInfo.localFolders) {
|
||||
if (trustInfo.uri === folder.toString()) {
|
||||
return trustInfo.trustState;
|
||||
}
|
||||
}
|
||||
|
||||
return WorkspaceTrustState.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkspaceTrustRequestModel extends Disposable implements IWorkspaceTrustRequestModel {
|
||||
trustRequest: IWorkspaceTrustRequest | undefined;
|
||||
|
||||
_onDidInitiateRequest = this._register(new Emitter<void>());
|
||||
onDidInitiateRequest: Event<void> = this._onDidInitiateRequest.event;
|
||||
|
||||
_onDidCompleteRequest = this._register(new Emitter<WorkspaceTrustState | undefined>());
|
||||
onDidCompleteRequest = this._onDidCompleteRequest.event;
|
||||
|
||||
initiateRequest(request: IWorkspaceTrustRequest): void {
|
||||
if (this.trustRequest && (!request.immediate || this.trustRequest.immediate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.trustRequest = request;
|
||||
this._onDidInitiateRequest.fire();
|
||||
}
|
||||
|
||||
completeRequest(trustState?: WorkspaceTrustState): void {
|
||||
this.trustRequest = undefined;
|
||||
this._onDidCompleteRequest.fire(trustState);
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkspaceTrustService extends Disposable implements IWorkspaceTrustService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
private readonly dataModel: IWorkspaceTrustModel;
|
||||
readonly requestModel: IWorkspaceTrustRequestModel;
|
||||
|
||||
private readonly _onDidChangeTrustState = this._register(new Emitter<WorkspaceTrustStateChangeEvent>());
|
||||
readonly onDidChangeTrustState = this._onDidChangeTrustState.event;
|
||||
|
||||
private _currentTrustState: WorkspaceTrustState = WorkspaceTrustState.Unknown;
|
||||
private _inFlightResolver?: (trustState: WorkspaceTrustState) => void;
|
||||
private _trustRequestPromise?: Promise<WorkspaceTrustState>;
|
||||
private _workspace: IWorkspace;
|
||||
|
||||
private readonly _ctxWorkspaceTrustState: IContextKey<WorkspaceTrustState>;
|
||||
private readonly _ctxWorkspaceTrustPendingRequest: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
|
||||
@IConfigurationService readonly configurationService: IConfigurationService,
|
||||
@IContextKeyService readonly contextKeyService: IContextKeyService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.dataModel = this._register(new WorkspaceTrustModel(this.storageService));
|
||||
this.requestModel = this._register(new WorkspaceTrustRequestModel());
|
||||
|
||||
this._workspace = this.workspaceService.getWorkspace();
|
||||
this._currentTrustState = this.calculateWorkspaceTrustState();
|
||||
|
||||
this._register(this.dataModel.onDidChangeTrustState(() => this.currentTrustState = this.calculateWorkspaceTrustState()));
|
||||
this._register(this.requestModel.onDidCompleteRequest((trustState) => this.onTrustRequestCompleted(trustState)));
|
||||
|
||||
this._ctxWorkspaceTrustState = WorkspaceTrustContext.TrustState.bindTo(contextKeyService);
|
||||
this._ctxWorkspaceTrustPendingRequest = WorkspaceTrustContext.PendingRequest.bindTo(contextKeyService);
|
||||
this._ctxWorkspaceTrustState.set(this.currentTrustState);
|
||||
}
|
||||
|
||||
private get currentTrustState(): WorkspaceTrustState {
|
||||
return this._currentTrustState;
|
||||
}
|
||||
|
||||
private set currentTrustState(trustState: WorkspaceTrustState) {
|
||||
if (this._currentTrustState === trustState) { return; }
|
||||
const previousState = this._currentTrustState;
|
||||
this._currentTrustState = trustState;
|
||||
|
||||
this._onDidChangeTrustState.fire({ previousTrustState: previousState, currentTrustState: this._currentTrustState });
|
||||
}
|
||||
|
||||
private calculateWorkspaceTrustState(): WorkspaceTrustState {
|
||||
if (!this.isWorkspaceTrustEnabled()) {
|
||||
return WorkspaceTrustState.Trusted;
|
||||
}
|
||||
|
||||
if (this.workspaceService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
return WorkspaceTrustState.Trusted;
|
||||
}
|
||||
|
||||
let state = undefined;
|
||||
for (const folder of this._workspace.folders) {
|
||||
const folderTrust = this.dataModel.getFolderTrustState(folder.uri);
|
||||
|
||||
switch (folderTrust) {
|
||||
case WorkspaceTrustState.Untrusted:
|
||||
return WorkspaceTrustState.Untrusted;
|
||||
case WorkspaceTrustState.Unknown:
|
||||
state = folderTrust;
|
||||
break;
|
||||
case WorkspaceTrustState.Trusted:
|
||||
if (state === undefined) {
|
||||
state = folderTrust;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return state ?? WorkspaceTrustState.Unknown;
|
||||
}
|
||||
|
||||
private onTrustRequestCompleted(trustState?: WorkspaceTrustState): void {
|
||||
if (this._inFlightResolver) {
|
||||
this._inFlightResolver(trustState === undefined ? this.currentTrustState : trustState);
|
||||
}
|
||||
|
||||
this._inFlightResolver = undefined;
|
||||
this._trustRequestPromise = undefined;
|
||||
|
||||
if (trustState === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._workspace.folders.forEach(folder => {
|
||||
this.dataModel.setFolderTrustState(folder.uri, trustState);
|
||||
});
|
||||
|
||||
this._ctxWorkspaceTrustPendingRequest.set(false);
|
||||
this._ctxWorkspaceTrustState.set(trustState);
|
||||
}
|
||||
|
||||
getWorkspaceTrustState(): WorkspaceTrustState {
|
||||
return this.currentTrustState;
|
||||
}
|
||||
|
||||
isWorkspaceTrustEnabled(): boolean {
|
||||
return this.configurationService.getValue<boolean>(WORKSPACE_TRUST_ENABLED) ?? false;
|
||||
}
|
||||
|
||||
async requireWorkspaceTrust(request?: IWorkspaceTrustRequest): Promise<WorkspaceTrustState> {
|
||||
if (this.currentTrustState === WorkspaceTrustState.Trusted) {
|
||||
return this.currentTrustState;
|
||||
}
|
||||
if (this.currentTrustState === WorkspaceTrustState.Untrusted && !request?.immediate) {
|
||||
return this.currentTrustState;
|
||||
}
|
||||
|
||||
if (this._trustRequestPromise) {
|
||||
if (request?.immediate &&
|
||||
this.requestModel.trustRequest &&
|
||||
!this.requestModel.trustRequest.immediate) {
|
||||
this.requestModel.initiateRequest(request);
|
||||
}
|
||||
|
||||
return this._trustRequestPromise;
|
||||
}
|
||||
|
||||
this._trustRequestPromise = new Promise(resolve => {
|
||||
this._inFlightResolver = resolve;
|
||||
});
|
||||
|
||||
this.requestModel.initiateRequest(request);
|
||||
this._ctxWorkspaceTrustPendingRequest.set(true);
|
||||
|
||||
return this._trustRequestPromise;
|
||||
}
|
||||
|
||||
async resetWorkspaceTrust(): Promise<WorkspaceTrustState> {
|
||||
if (this.currentTrustState !== WorkspaceTrustState.Unknown) {
|
||||
this._workspace.folders.forEach(folder => {
|
||||
this.dataModel.setFolderTrustState(folder.uri, WorkspaceTrustState.Unknown);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(WorkspaceTrustState.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IWorkspaceTrustService, WorkspaceTrustService);
|
||||
|
|
|
@ -21,6 +21,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
|||
private _accounts = new Map<string, string[]>(); // Map account name to session ids
|
||||
private _sessions = new Map<string, string>(); // Map account id to name
|
||||
|
||||
private _hasInitializedSessions = false;
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: ExtHostAuthenticationShape,
|
||||
public readonly id: string,
|
||||
|
@ -33,11 +35,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
|||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
return this.registerCommandsAndContextMenuItems();
|
||||
}
|
||||
|
||||
public hasSessions(): boolean {
|
||||
return !!this._sessions.size;
|
||||
}
|
||||
|
@ -83,15 +80,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
|||
quickPick.show();
|
||||
}
|
||||
|
||||
private async registerCommandsAndContextMenuItems(): Promise<void> {
|
||||
try {
|
||||
const sessions = await this._proxy.$getSessions(this.id);
|
||||
sessions.forEach(session => this.registerSession(session));
|
||||
} catch (_) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private registerSession(session: modes.AuthenticationSession) {
|
||||
this._sessions.set(session.id, session.account.label);
|
||||
|
||||
|
@ -123,7 +111,13 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
|||
}
|
||||
|
||||
async getSessions(): Promise<ReadonlyArray<modes.AuthenticationSession>> {
|
||||
return this._proxy.$getSessions(this.id);
|
||||
const sessions = await this._proxy.$getSessions(this.id);
|
||||
if (!this._hasInitializedSessions) {
|
||||
sessions.forEach(session => this.registerSession(session));
|
||||
this._hasInitializedSessions = true;
|
||||
}
|
||||
|
||||
return sessions;
|
||||
}
|
||||
|
||||
async updateSessionItems(event: modes.AuthenticationSessionsChangeEvent): Promise<void> {
|
||||
|
@ -195,7 +189,6 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
|||
|
||||
async $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): Promise<void> {
|
||||
const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageService, this.quickInputService, this.dialogService);
|
||||
await provider.initialize();
|
||||
this.authenticationService.registerAuthenticationProvider(id, provider);
|
||||
}
|
||||
|
||||
|
@ -268,7 +261,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
|||
|
||||
async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone: boolean, clearSessionPreference: boolean }): Promise<modes.AuthenticationSession | undefined> {
|
||||
const orderedScopes = scopes.sort().join(' ');
|
||||
const sessions = (await this.authenticationService.getSessions(providerId)).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes);
|
||||
const sessions = (await this.authenticationService.getSessions(providerId, true)).filter(session => session.scopes.slice().sort().join(' ') === orderedScopes);
|
||||
|
||||
const silent = !options.createIfNone;
|
||||
let session: modes.AuthenticationSession | undefined;
|
||||
|
@ -300,7 +293,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
|||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
session = await this.authenticationService.login(providerId, scopes);
|
||||
session = await this.authenticationService.login(providerId, scopes, true);
|
||||
await this.setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
|
||||
} else {
|
||||
await this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName);
|
||||
|
|
|
@ -129,13 +129,13 @@ export class MainThreadFileSystemEventService {
|
|||
}
|
||||
} else {
|
||||
if (operation === FileOperation.CREATE) {
|
||||
message = localize('ask.N.create', "{0} extensions want to make refactoring changes with this file creation", data.extensionNames.length);
|
||||
message = localize({ key: 'ask.N.create', comment: ['{0} is a number, e.g "3 extensions want..."'] }, "{0} extensions want to make refactoring changes with this file creation", data.extensionNames.length);
|
||||
} else if (operation === FileOperation.COPY) {
|
||||
message = localize('ask.N.copy', "{0} extensions want to make refactoring changes with this file copy", data.extensionNames.length);
|
||||
message = localize({ key: 'ask.N.copy', comment: ['{0} is a number, e.g "3 extensions want..."'] }, "{0} extensions want to make refactoring changes with this file copy", data.extensionNames.length);
|
||||
} else if (operation === FileOperation.MOVE) {
|
||||
message = localize('ask.N.move', "{0} extensions want to make refactoring changes with this file move", data.extensionNames.length);
|
||||
message = localize({ key: 'ask.N.move', comment: ['{0} is a number, e.g "3 extensions want..."'] }, "{0} extensions want to make refactoring changes with this file move", data.extensionNames.length);
|
||||
} else /* if (operation === FileOperation.DELETE) */ {
|
||||
message = localize('ask.N.delete', "{0} extensions want to make refactoring changes with this file deletion", data.extensionNames.length);
|
||||
message = localize({ key: 'ask.N.delete', comment: ['{0} is a number, e.g "3 extensions want..."'] }, "{0} extensions want to make refactoring changes with this file deletion", data.extensionNames.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { addIdToOutput, CellEditType, ICellEditOperation, ICellOutputEdit, ITransformedDisplayOutputDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { addIdToOutput, CellEditType, ICellEditOperation, ICellOutputEdit, ITransformedDisplayOutputDto, notebookDocumentMetadataDefaults, NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
function es5ClassCompat(target: Function): any {
|
||||
|
@ -2813,6 +2813,72 @@ export enum ColorThemeKind {
|
|||
|
||||
//#region Notebook
|
||||
|
||||
export class NotebookCellMetadata {
|
||||
|
||||
constructor(
|
||||
readonly editable?: boolean,
|
||||
readonly breakpointMargin?: boolean,
|
||||
readonly runnable?: boolean,
|
||||
readonly hasExecutionOrder?: boolean,
|
||||
readonly executionOrder?: number,
|
||||
readonly runState?: NotebookCellRunState,
|
||||
readonly runStartTime?: number,
|
||||
readonly statusMessage?: string,
|
||||
readonly lastRunDuration?: number,
|
||||
readonly inputCollapsed?: boolean,
|
||||
readonly outputCollapsed?: boolean,
|
||||
readonly custom?: Record<string, any>,
|
||||
) { }
|
||||
|
||||
with(change: Partial<Omit<NotebookCellMetadata, 'with'>>): NotebookCellMetadata {
|
||||
return new NotebookCellMetadata(
|
||||
change.editable ?? this.editable,
|
||||
change.breakpointMargin ?? this.breakpointMargin,
|
||||
change.runnable ?? this.runnable,
|
||||
change.hasExecutionOrder ?? this.hasExecutionOrder,
|
||||
change.executionOrder ?? this.executionOrder,
|
||||
change.runState ?? this.runState,
|
||||
change.runStartTime ?? this.runStartTime,
|
||||
change.statusMessage ?? this.statusMessage,
|
||||
change.lastRunDuration ?? this.lastRunDuration,
|
||||
change.inputCollapsed ?? this.inputCollapsed,
|
||||
change.outputCollapsed ?? this.outputCollapsed,
|
||||
change.custom ?? this.custom
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookDocumentMetadata {
|
||||
|
||||
constructor(
|
||||
readonly editable: boolean = true,
|
||||
readonly runnable: boolean = true,
|
||||
readonly cellEditable: boolean = true,
|
||||
readonly cellRunnable: boolean = true,
|
||||
readonly cellHasExecutionOrder: boolean = true,
|
||||
readonly displayOrder: vscode.GlobPattern[] = NOTEBOOK_DISPLAY_ORDER,
|
||||
readonly custom: { [key: string]: any; } = {},
|
||||
readonly runState: NotebookRunState = NotebookRunState.Idle,
|
||||
readonly trusted: boolean = true,
|
||||
readonly languages: string[] = [],
|
||||
) { }
|
||||
|
||||
with(change: Partial<Omit<NotebookDocumentMetadata, 'with'>>) {
|
||||
return new NotebookDocumentMetadata(
|
||||
change.editable ?? this.editable,
|
||||
change.runnable ?? this.runnable,
|
||||
change.cellEditable ?? this.cellEditable,
|
||||
change.cellRunnable ?? this.cellRunnable,
|
||||
change.cellHasExecutionOrder ?? this.cellHasExecutionOrder,
|
||||
change.displayOrder ?? this.displayOrder,
|
||||
change.custom ?? this.custom,
|
||||
change.runState ?? this.runState,
|
||||
change.trusted ?? this.trusted,
|
||||
change.languages ?? this.languages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellOutputItem {
|
||||
|
||||
static isNotebookCellOutputItem(obj: unknown): obj is vscode.NotebookCellOutputItem {
|
||||
|
|
|
@ -182,6 +182,11 @@ const apiMenus: IAPIMenu[] = [
|
|||
id: MenuId.TimelineItemContext,
|
||||
description: localize('view.timelineContext', "The Timeline view item context menu")
|
||||
},
|
||||
{
|
||||
key: 'ports/item/context',
|
||||
id: MenuId.TunnelContext,
|
||||
description: localize('view.tunnelContext', "The Ports view item context menu")
|
||||
}
|
||||
];
|
||||
|
||||
namespace schema {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
|||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_URI } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_URI } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
|
||||
export class OpenFileAction extends Action {
|
||||
|
||||
|
@ -255,8 +255,9 @@ class WorkspaceTrustManageAction extends Action2 {
|
|||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.manageTrust',
|
||||
title: { value: nls.localize('resetTrustAction', "Manage Trusted Workspaces"), original: 'Manage Trusted Workspaces' },
|
||||
title: { value: nls.localize('manageTrustAction', "Manage Workspace Trust"), original: 'Manage Workspace Trust' },
|
||||
precondition: ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true),
|
||||
category: nls.localize('workspacesCategory', "Workspaces"),
|
||||
f1: true,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -259,6 +259,11 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem {
|
|||
}
|
||||
});
|
||||
|
||||
if (providers.length && !menus.length) {
|
||||
const noAccountsAvailableAction = disposables.add(new Action('noAccountsAvailable', localize('noAccounts', "You are not signed in to any accounts"), undefined, false));
|
||||
menus.push(noAccountsAvailableAction);
|
||||
}
|
||||
|
||||
if (menus.length && otherCommands.length) {
|
||||
menus.push(disposables.add(new Separator()));
|
||||
}
|
||||
|
|
|
@ -199,8 +199,6 @@ export class ActivityActionViewItem extends BaseActionViewItem {
|
|||
|
||||
this.container = container;
|
||||
|
||||
// Make the container tab-able for keyboard navigation
|
||||
this.container.tabIndex = 0;
|
||||
this.container.setAttribute('role', 'tab');
|
||||
|
||||
// Try hard to prevent keyboard only focus feedback when using mouse
|
||||
|
@ -647,10 +645,6 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
|||
});
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.container.focus();
|
||||
}
|
||||
|
||||
protected updateChecked(): void {
|
||||
if (this.getAction().checked) {
|
||||
this.container.classList.add('checked');
|
||||
|
|
|
@ -427,15 +427,9 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
|||
}
|
||||
|
||||
async _revealElement(element: IFileStat | IWorkspaceFolder, options: IEditorOptions, sideBySide: boolean): Promise<boolean> {
|
||||
let resource: URI | undefined;
|
||||
if (isWorkspaceFolder(element)) {
|
||||
resource = element.uri;
|
||||
} else if (!element.isDirectory) {
|
||||
resource = element.resource;
|
||||
}
|
||||
if (resource) {
|
||||
if (!isWorkspaceFolder(element) && element.isFile) {
|
||||
this._onWillPickElement.fire();
|
||||
await this._editorService.openEditor({ resource, options }, sideBySide ? SIDE_GROUP : undefined);
|
||||
await this._editorService.openEditor({ resource: element.resource, options }, sideBySide ? SIDE_GROUP : undefined);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
} from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar';
|
||||
import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService';
|
||||
import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID } from 'vs/workbench/contrib/debug/browser/debugCommands';
|
||||
import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands';
|
||||
import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider';
|
||||
import { IViewsRegistry, Extensions as ViewExtensions, IViewContainersRegistry, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views';
|
||||
import { isMacintosh, isWeb } from 'vs/base/common/platform';
|
||||
|
@ -123,6 +123,8 @@ function registerCommandsAndActions(): void {
|
|||
registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, nls.localize('inlineBreakpoint', "Inline Breakpoint"));
|
||||
registerDebugCommandPaletteItem(DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))));
|
||||
registerDebugCommandPaletteItem(DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))));
|
||||
registerDebugCommandPaletteItem(SELECT_AND_START_ID, SELECT_AND_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))));
|
||||
|
||||
|
||||
// Debug callstack context menu
|
||||
const registerDebugViewMenuItem = (menuId: MenuId, id: string, title: string, order: number, when?: ContextKeyExpression, precondition?: ContextKeyExpression, group = 'navigation') => {
|
||||
|
|
|
@ -165,6 +165,10 @@ export class ReplFilterActionViewItem extends BaseActionViewItem {
|
|||
return this.filterInputBox.getHistory();
|
||||
}
|
||||
|
||||
get trapsArrowNavigation(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
private clearFilterText(): void {
|
||||
this.filterInputBox.value = '';
|
||||
}
|
||||
|
|
|
@ -704,6 +704,7 @@ declare module DebugProtocol {
|
|||
The request configures the debuggers response to thrown exceptions.
|
||||
If an exception is configured to break, a 'stopped' event is fired (with reason 'exception').
|
||||
Clients should only call this request if the capability 'exceptionBreakpointFilters' returns one or more filters.
|
||||
If a filter or filter option is invalid (e.g. due to an invalid 'condition'), the request should fail with an 'ErrorResponse' explaining the problem(s).
|
||||
*/
|
||||
export interface SetExceptionBreakpointsRequest extends Request {
|
||||
// command: 'setExceptionBreakpoints';
|
||||
|
@ -1629,10 +1630,14 @@ declare module DebugProtocol {
|
|||
filter: string;
|
||||
/** The name of the filter option. This will be shown in the UI. */
|
||||
label: string;
|
||||
/** An optional help text providing additional information about the exception filter. This string is typically shown as a hover and must be translated. */
|
||||
description?: string;
|
||||
/** Initial value of the filter option. If not specified a value 'false' is assumed. */
|
||||
default?: boolean;
|
||||
/** Controls whether a condition can be specified for this filter option. If false or missing, a condition can not be set. */
|
||||
supportsCondition?: boolean;
|
||||
/** An optional help text providing information about the condition. This string is shown as the placeholder text for a text box and must be translated. */
|
||||
conditionDescription?: string;
|
||||
}
|
||||
|
||||
/** A structured message object. Used to return errors from requests. */
|
||||
|
@ -1774,6 +1779,8 @@ declare module DebugProtocol {
|
|||
endLine?: number;
|
||||
/** An optional end column of the range covered by the stack frame. */
|
||||
endColumn?: number;
|
||||
/** Indicates whether this frame can be restarted with the 'restart' request. Clients should only use this if the debug adapter supports the 'restart' request (capability 'supportsRestartRequest' is true). */
|
||||
canRestart?: boolean;
|
||||
/** Optional memory reference for the current instruction pointer in this frame. */
|
||||
instructionPointerReference?: string;
|
||||
/** The module associated with this frame, if any. */
|
||||
|
|
|
@ -32,7 +32,7 @@ import { timeout } from 'vs/base/common/async';
|
|||
import { TestExtensionService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { OS } from 'vs/base/common/platform';
|
||||
import { IWorkspaceTrustService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { TestWorkspaceTrustService } from 'vs/platform/workspace/test/common/testWorkspaceTrust';
|
||||
import { TestWorkspaceTrustService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
|
||||
|
||||
interface ExperimentSettings {
|
||||
enabled?: boolean;
|
||||
|
|
|
@ -56,7 +56,7 @@ import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/
|
|||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { IWorkspaceTrustService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { TestWorkspaceTrustService } from 'vs/platform/workspace/test/common/testWorkspaceTrust';
|
||||
import { TestWorkspaceTrustService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
|
||||
|
||||
let instantiationService: TestInstantiationService;
|
||||
let installEvent: Emitter<InstallExtensionEvent>,
|
||||
|
|
|
@ -283,6 +283,10 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
|
|||
}
|
||||
}
|
||||
|
||||
get trapsArrowNavigation(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
private clearFilterText(): void {
|
||||
if (this.filterInputBox) {
|
||||
this.filterInputBox.value = '';
|
||||
|
|
|
@ -462,6 +462,7 @@ class OutputAutomaticPortForwarding extends Disposable {
|
|||
class ProcAutomaticPortForwarding extends Disposable {
|
||||
private candidateListener: IDisposable | undefined;
|
||||
private autoForwarded: Set<string> = new Set();
|
||||
private notifiedOnly: Set<string> = new Set();
|
||||
private notifier: OnAutoForwardedAction;
|
||||
private initialCandidates: Set<string> = new Set();
|
||||
private portsFeatures: IDisposable | undefined;
|
||||
|
@ -543,18 +544,18 @@ class ProcAutomaticPortForwarding extends Disposable {
|
|||
if (this.initialCandidates.has(address)) {
|
||||
return undefined;
|
||||
}
|
||||
const alreadyForwarded = mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, value.host, value.port);
|
||||
if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.detected, value.host, value.port)) {
|
||||
return undefined;
|
||||
}
|
||||
if (mapHasAddressLocalhostOrAllInterfaces(this.remoteExplorerService.tunnelModel.forwarded, value.host, value.port)) {
|
||||
return undefined;
|
||||
}
|
||||
if (this.portsAttributes.getAttributes(value.port)?.onAutoForward === OnPortForward.Ignore) {
|
||||
return undefined;
|
||||
}
|
||||
const forwarded = await this.remoteExplorerService.forward(value, undefined, undefined, undefined, undefined, undefined, false);
|
||||
if (forwarded) {
|
||||
if (!alreadyForwarded && forwarded) {
|
||||
this.autoForwarded.add(address);
|
||||
} else if (forwarded) {
|
||||
this.notifiedOnly.add(address);
|
||||
}
|
||||
return forwarded;
|
||||
}))).filter(tunnel => !!tunnel);
|
||||
|
@ -571,6 +572,9 @@ class ProcAutomaticPortForwarding extends Disposable {
|
|||
this.remoteExplorerService.close(value);
|
||||
this.autoForwarded.delete(key);
|
||||
removedPorts.push(value.port);
|
||||
} else if (this.notifiedOnly.has(key)) {
|
||||
this.notifiedOnly.delete(key);
|
||||
removedPorts.push(value.port);
|
||||
} else if (this.initialCandidates.has(key)) {
|
||||
this.initialCandidates.delete(key);
|
||||
}
|
||||
|
|
|
@ -812,23 +812,26 @@ namespace LabelTunnelAction {
|
|||
export const LABEL = nls.localize('remote.tunnel.label', "Set Label");
|
||||
|
||||
export function handler(): ICommandHandler {
|
||||
return async (accessor, arg) => {
|
||||
return async (accessor, arg): Promise<{ port: number, label: string } | undefined> => {
|
||||
const context = (arg !== undefined || arg instanceof TunnelItem) ? arg : accessor.get(IContextKeyService).getContextKeyValue(TunnelViewSelectionKeyName);
|
||||
if (context instanceof TunnelItem) {
|
||||
const remoteExplorerService = accessor.get(IRemoteExplorerService);
|
||||
remoteExplorerService.setEditable(context, {
|
||||
onFinish: async (value, success) => {
|
||||
if (success) {
|
||||
remoteExplorerService.tunnelModel.name(context.remoteHost, context.remotePort, value);
|
||||
}
|
||||
remoteExplorerService.setEditable(context, null);
|
||||
},
|
||||
validationMessage: () => null,
|
||||
placeholder: nls.localize('remote.tunnelsView.labelPlaceholder', "Port label"),
|
||||
startingValue: context.name
|
||||
return new Promise(resolve => {
|
||||
const remoteExplorerService = accessor.get(IRemoteExplorerService);
|
||||
remoteExplorerService.setEditable(context, {
|
||||
onFinish: async (value, success) => {
|
||||
if (success) {
|
||||
remoteExplorerService.tunnelModel.name(context.remoteHost, context.remotePort, value);
|
||||
}
|
||||
remoteExplorerService.setEditable(context, null);
|
||||
resolve(success ? { port: context.remotePort, label: value } : undefined);
|
||||
},
|
||||
validationMessage: () => null,
|
||||
placeholder: nls.localize('remote.tunnelsView.labelPlaceholder', "Port label"),
|
||||
startingValue: context.name
|
||||
});
|
||||
});
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import { Action2, ICommandAction, MenuId, MenuRegistry, registerAction2, SyncAct
|
|||
import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ContextKeyRegexExpr, ContextKeyEqualsExpr, ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ContextKeyEqualsExpr, ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
@ -637,10 +637,6 @@ const viewDescriptor: IViewDescriptor = {
|
|||
mnemonicTitle: nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search"),
|
||||
keybindings: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F,
|
||||
// View: Show Search is used for the keybindings in the View menu and the sidebar, but Find In Files should run the actual action.
|
||||
// If the context key is clearly false (ContextKeyFalseExpression), the keybinding won't appear. Instead we use a regex that never is true.
|
||||
// See #116188, #115556, #115511
|
||||
when: ContextKeyRegexExpr.create(`never`, /(?!x)x/),
|
||||
},
|
||||
order: 1
|
||||
}
|
||||
|
@ -714,7 +710,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
|||
]
|
||||
},
|
||||
id: Constants.FindInFilesActionId,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
// Give more weightage to this keybinding than of `View: Show Search` keybinding. See #116188, #115556, #115511
|
||||
weight: KeybindingWeight.WorkbenchContrib + 1,
|
||||
when: null,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F,
|
||||
handler: FindInFilesCommand
|
||||
|
|
|
@ -436,7 +436,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
|||
private registerGlobalActivityActions(): void {
|
||||
CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates(this.environmentService.sessionId));
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
group: '7_update',
|
||||
command: {
|
||||
id: 'update.check',
|
||||
title: nls.localize('checkForUpdates', "Check for Updates...")
|
||||
|
@ -446,7 +446,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
|||
|
||||
CommandsRegistry.registerCommand('update.checking', () => { });
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
group: '7_update',
|
||||
command: {
|
||||
id: 'update.checking',
|
||||
title: nls.localize('checkingForUpdates', "Checking for Updates..."),
|
||||
|
@ -457,7 +457,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
|||
|
||||
CommandsRegistry.registerCommand('update.downloadNow', () => this.updateService.downloadUpdate());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
group: '7_update',
|
||||
command: {
|
||||
id: 'update.downloadNow',
|
||||
title: nls.localize('download update_1', "Download Update (1)")
|
||||
|
@ -467,7 +467,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
|||
|
||||
CommandsRegistry.registerCommand('update.downloading', () => { });
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
group: '7_update',
|
||||
command: {
|
||||
id: 'update.downloading',
|
||||
title: nls.localize('DownloadingUpdate', "Downloading Update..."),
|
||||
|
@ -478,7 +478,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
|||
|
||||
CommandsRegistry.registerCommand('update.install', () => this.updateService.applyUpdate());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
group: '7_update',
|
||||
command: {
|
||||
id: 'update.install',
|
||||
title: nls.localize('installUpdate...', "Install Update... (1)")
|
||||
|
@ -488,7 +488,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
|||
|
||||
CommandsRegistry.registerCommand('update.updating', () => { });
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
group: '7_update',
|
||||
command: {
|
||||
id: 'update.updating',
|
||||
title: nls.localize('installingUpdate', "Installing Update..."),
|
||||
|
@ -499,7 +499,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
|
|||
|
||||
CommandsRegistry.registerCommand('update.restart', () => this.updateService.quitAndInstall());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '6_update',
|
||||
group: '7_update',
|
||||
command: {
|
||||
id: 'update.restart',
|
||||
title: nls.localize('restartToUpdate', "Restart to Update (1)")
|
||||
|
@ -536,7 +536,7 @@ export class SwitchProductQualityContribution extends Disposable implements IWor
|
|||
menu: {
|
||||
id: MenuId.GlobalActivity,
|
||||
when: IsWebContext,
|
||||
group: '6_update',
|
||||
group: '7_update',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
|||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkspaceTrustService, WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_URI, WorkspaceTrustState, WorkspaceTrustStateChangeEvent, workspaceTrustStateToString } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { IWorkspaceTrustService, WorkspaceTrustState, WorkspaceTrustStateChangeEvent, workspaceTrustStateToString } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { IActivityService, IconBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
@ -20,11 +20,11 @@ import { ThemeColor } from 'vs/workbench/api/common/extHostTypes';
|
|||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { WorkspaceTrustFileSystemProvider } from 'vs/workbench/contrib/workspace/common/workspaceTrustFileSystemProvider';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar';
|
||||
import { WorkspaceTrustContext, WORKSPACE_TRUST_ENABLED, WORKSPACE_TRUST_URI } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
|
||||
const workspaceTrustIcon = registerIcon('workspace-trust-icon', Codicon.shield, localize('workspaceTrustIcon', "Icon for workspace trust badge."));
|
||||
|
||||
|
@ -200,7 +200,7 @@ registerAction2(class extends Action2 {
|
|||
menu: {
|
||||
id: MenuId.GlobalActivity,
|
||||
when: WorkspaceTrustContext.PendingRequest,
|
||||
group: '7_trust',
|
||||
group: '6_workspace_trust',
|
||||
order: 10
|
||||
},
|
||||
});
|
||||
|
@ -240,7 +240,7 @@ registerAction2(class extends Action2 {
|
|||
menu: {
|
||||
id: MenuId.GlobalActivity,
|
||||
when: WorkspaceTrustContext.PendingRequest,
|
||||
group: '7_trust',
|
||||
group: '6_workspace_trust',
|
||||
order: 20
|
||||
},
|
||||
});
|
||||
|
@ -264,53 +264,19 @@ registerAction2(class extends Action2 {
|
|||
}
|
||||
});
|
||||
|
||||
// Reset Workspace Trust
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.trust.reset',
|
||||
title: {
|
||||
original: 'Reset Workspace Trust',
|
||||
value: localize('reset', "Reset Workspace Trust")
|
||||
},
|
||||
category: localize('workspacesCategory', "Workspaces"),
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('empty').negate(), WorkspaceTrustContext.TrustState.isEqualTo(WorkspaceTrustState.Unknown).negate())
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor) {
|
||||
const dialogService = accessor.get(IDialogService);
|
||||
const workspaceTrustService = accessor.get(IWorkspaceTrustService);
|
||||
|
||||
const result = await dialogService.confirm({
|
||||
message: localize('reset', "Reset Workspace Trust"),
|
||||
detail: localize('confirmResetWorkspaceTrust', "Resetting workspace trust to the workspace will disable features that may pose a security risk if the contents of the workspace cannot be trusted. Are you sure you want to reset trust this workspace?"),
|
||||
primaryButton: localize('yesGrant', 'Yes'),
|
||||
secondaryButton: localize('noGrant', 'No')
|
||||
});
|
||||
|
||||
if (result.confirmed) {
|
||||
workspaceTrustService.resetWorkspaceTrust();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Manage Workspace Trust
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.trust.manage',
|
||||
title: {
|
||||
original: 'Manage Trusted Workspaces',
|
||||
value: localize('manageWorkspaceTrust', "Manage Trusted Workspaces")
|
||||
original: 'Manage Workspace Trust',
|
||||
value: localize('manageWorkspaceTrust', "Manage Workspace Trust")
|
||||
},
|
||||
category: localize('workspacesCategory', "Workspaces"),
|
||||
menu: {
|
||||
id: MenuId.GlobalActivity,
|
||||
group: '7_trust',
|
||||
group: '6_workspace_trust',
|
||||
order: 40,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true), WorkspaceTrustContext.PendingRequest.negate())
|
||||
},
|
||||
|
@ -327,9 +293,9 @@ registerAction2(class extends Action2 {
|
|||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
command: {
|
||||
id: 'workbench.trust.manage',
|
||||
title: localize('manageWorkspaceTrustPending', "Manage Trusted Workspaces (1)"),
|
||||
title: localize('manageWorkspaceTrustPending', "Manage Workspace Trust (1)"),
|
||||
},
|
||||
group: '7_trust',
|
||||
group: '6_workspace_trust',
|
||||
order: 40,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true), WorkspaceTrustContext.PendingRequest)
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ import { FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities
|
|||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { WORKSPACE_TRUST_STORAGE_KEY } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { WORKSPACE_TRUST_STORAGE_KEY } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
|
||||
const WORKSPACE_TRUST_SCHEMA = 'workspaceTrust';
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/exte
|
|||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
@ -124,10 +124,10 @@ export interface IAuthenticationService {
|
|||
declaredProviders: AuthenticationProviderInformation[];
|
||||
readonly onDidChangeDeclaredProviders: Event<AuthenticationProviderInformation[]>;
|
||||
|
||||
getSessions(providerId: string): Promise<ReadonlyArray<AuthenticationSession>>;
|
||||
getSessions(providerId: string, activateImmediate?: boolean): Promise<ReadonlyArray<AuthenticationSession>>;
|
||||
getLabel(providerId: string): string;
|
||||
supportsMultipleAccounts(providerId: string): boolean;
|
||||
login(providerId: string, scopes: string[]): Promise<AuthenticationSession>;
|
||||
login(providerId: string, scopes: string[], activateImmediate?: boolean): Promise<AuthenticationSession>;
|
||||
logout(providerId: string, sessionId: string): Promise<void>;
|
||||
|
||||
manageTrustedExtensionsForAccount(providerId: string, accountName: string): Promise<void>;
|
||||
|
@ -192,7 +192,6 @@ const authenticationExtPoint = ExtensionsRegistry.registerExtensionPoint<Authent
|
|||
export class AuthenticationService extends Disposable implements IAuthenticationService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
private _placeholderMenuItem: IDisposable | undefined;
|
||||
private _noAccountsMenuItem: IDisposable | undefined;
|
||||
private _signInRequestItems = new Map<string, SessionRequestInfo>();
|
||||
private _sessionAccessRequestItems = new Map<string, { [extensionId: string]: { disposables: IDisposable[], possibleSessions: AuthenticationSession[] } }>();
|
||||
private _accountBadgeDisposable = this._register(new MutableDisposable());
|
||||
|
@ -278,29 +277,6 @@ export class AuthenticationService extends Disposable implements IAuthentication
|
|||
return this._authenticationProviders.has(id);
|
||||
}
|
||||
|
||||
private updateAccountsMenuItem(): void {
|
||||
let hasSession = false;
|
||||
this._authenticationProviders.forEach(async provider => {
|
||||
hasSession = hasSession || provider.hasSessions();
|
||||
});
|
||||
|
||||
if (hasSession && this._noAccountsMenuItem) {
|
||||
this._noAccountsMenuItem.dispose();
|
||||
this._noAccountsMenuItem = undefined;
|
||||
}
|
||||
|
||||
if (!hasSession && !this._noAccountsMenuItem) {
|
||||
this._noAccountsMenuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
|
||||
group: '0_accounts',
|
||||
command: {
|
||||
id: 'noAccounts',
|
||||
title: nls.localize('noAccounts', "You are not signed in to any accounts"),
|
||||
precondition: ContextKeyExpr.false()
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerAuthenticationProvider(id: string, authenticationProvider: MainThreadAuthenticationProvider): void {
|
||||
this._authenticationProviders.set(id, authenticationProvider);
|
||||
this._onDidRegisterAuthenticationProvider.fire({ id, label: authenticationProvider.label });
|
||||
|
@ -309,8 +285,6 @@ export class AuthenticationService extends Disposable implements IAuthentication
|
|||
this._placeholderMenuItem.dispose();
|
||||
this._placeholderMenuItem = undefined;
|
||||
}
|
||||
|
||||
this.updateAccountsMenuItem();
|
||||
}
|
||||
|
||||
unregisterAuthenticationProvider(id: string): void {
|
||||
|
@ -319,7 +293,6 @@ export class AuthenticationService extends Disposable implements IAuthentication
|
|||
provider.dispose();
|
||||
this._authenticationProviders.delete(id);
|
||||
this._onDidUnregisterAuthenticationProvider.fire({ id, label: provider.label });
|
||||
this.updateAccountsMenuItem();
|
||||
|
||||
const accessRequests = this._sessionAccessRequestItems.get(id) || {};
|
||||
Object.keys(accessRequests).forEach(extensionId => {
|
||||
|
@ -343,7 +316,6 @@ export class AuthenticationService extends Disposable implements IAuthentication
|
|||
if (provider) {
|
||||
this._onDidChangeSessions.fire({ providerId: id, label: provider.label, event: event });
|
||||
await provider.updateSessionItems(event);
|
||||
this.updateAccountsMenuItem();
|
||||
|
||||
if (event.added) {
|
||||
await this.updateNewSessionRequests(provider);
|
||||
|
@ -693,8 +665,8 @@ export class AuthenticationService extends Disposable implements IAuthentication
|
|||
}
|
||||
}
|
||||
|
||||
private async tryActivateProvider(providerId: string): Promise<MainThreadAuthenticationProvider> {
|
||||
await this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(providerId));
|
||||
private async tryActivateProvider(providerId: string, activateImmediate: boolean): Promise<MainThreadAuthenticationProvider> {
|
||||
await this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(providerId), activateImmediate ? ActivationKind.Immediate : ActivationKind.Normal);
|
||||
let provider = this._authenticationProviders.get(providerId);
|
||||
if (provider) {
|
||||
return provider;
|
||||
|
@ -724,18 +696,18 @@ export class AuthenticationService extends Disposable implements IAuthentication
|
|||
return Promise.race([didRegister, didTimeout]);
|
||||
}
|
||||
|
||||
async getSessions(id: string): Promise<ReadonlyArray<AuthenticationSession>> {
|
||||
async getSessions(id: string, activateImmediate: boolean = false): Promise<ReadonlyArray<AuthenticationSession>> {
|
||||
try {
|
||||
const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id);
|
||||
const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, activateImmediate);
|
||||
return await authProvider.getSessions();
|
||||
} catch (_) {
|
||||
throw new Error(`No authentication provider '${id}' is currently registered.`);
|
||||
}
|
||||
}
|
||||
|
||||
async login(id: string, scopes: string[]): Promise<AuthenticationSession> {
|
||||
async login(id: string, scopes: string[], activateImmediate: boolean = false): Promise<AuthenticationSession> {
|
||||
try {
|
||||
const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id);
|
||||
const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, activateImmediate);
|
||||
return await authProvider.login(scopes);
|
||||
} catch (_) {
|
||||
throw new Error(`No authentication provider '${id}' is currently registered.`);
|
||||
|
|
|
@ -31,7 +31,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
|
|||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect';
|
||||
import { IWorkspaceTrustService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { TestWorkspaceTrustService } from 'vs/platform/workspace/test/common/testWorkspaceTrust';
|
||||
import { TestWorkspaceTrustService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
|
||||
|
||||
function createStorageService(instantiationService: TestInstantiationService): IStorageService {
|
||||
let service = instantiationService.get(IStorageService);
|
||||
|
|
|
@ -371,7 +371,9 @@ export class TunnelModel extends Disposable {
|
|||
return tunnel;
|
||||
}
|
||||
} else {
|
||||
existingTunnel.name = name;
|
||||
if (name) {
|
||||
existingTunnel.name = name;
|
||||
}
|
||||
this._onForwardPort.fire();
|
||||
return mapHasAddressLocalhostOrAllInterfaces(this.remoteTunnels, remote.host, remote.port);
|
||||
}
|
||||
|
|
290
src/vs/workbench/services/workspaces/common/workspaceTrust.ts
Normal file
290
src/vs/workbench/services/workspaces/common/workspaceTrust.ts
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceTrustModel, IWorkspaceTrustRequest, IWorkspaceTrustRequestModel, IWorkspaceTrustService, IWorkspaceTrustStateInfo, WorkspaceTrustState, WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
|
||||
export const WORKSPACE_TRUST_ENABLED = 'workspace.trustEnabled';
|
||||
export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key';
|
||||
export const WORKSPACE_TRUST_URI = URI.parse('workspaceTrust:/Trusted Workspaces');
|
||||
|
||||
export const WorkspaceTrustContext = {
|
||||
PendingRequest: new RawContextKey<boolean>('workspaceTrustPendingRequest', false),
|
||||
TrustState: new RawContextKey<WorkspaceTrustState>('workspaceTrustState', WorkspaceTrustState.Unknown)
|
||||
};
|
||||
|
||||
export class WorkspaceTrustModel extends Disposable implements IWorkspaceTrustModel {
|
||||
|
||||
private storageKey = WORKSPACE_TRUST_STORAGE_KEY;
|
||||
private trustStateInfo: IWorkspaceTrustStateInfo;
|
||||
|
||||
private readonly _onDidChangeTrustState = this._register(new Emitter<void>());
|
||||
readonly onDidChangeTrustState = this._onDidChangeTrustState.event;
|
||||
|
||||
constructor(
|
||||
private readonly storageService: IStorageService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.trustStateInfo = this.loadTrustInfo();
|
||||
this._register(this.storageService.onDidChangeValue(changeEvent => {
|
||||
if (changeEvent.key === this.storageKey) {
|
||||
this.onDidStorageChange();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private loadTrustInfo(): IWorkspaceTrustStateInfo {
|
||||
const infoAsString = this.storageService.get(this.storageKey, StorageScope.GLOBAL);
|
||||
|
||||
let result: IWorkspaceTrustStateInfo | undefined;
|
||||
try {
|
||||
if (infoAsString) {
|
||||
result = JSON.parse(infoAsString);
|
||||
}
|
||||
} catch { }
|
||||
|
||||
if (!result) {
|
||||
result = {
|
||||
localFolders: [],
|
||||
//trustedRemoteItems: []
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.localFolders) {
|
||||
result.localFolders = [];
|
||||
}
|
||||
|
||||
// if (!result.trustedRemoteItems) {
|
||||
// result.trustedRemoteItems = [];
|
||||
// }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private saveTrustInfo(): void {
|
||||
this.storageService.store(this.storageKey, JSON.stringify(this.trustStateInfo), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
}
|
||||
|
||||
private onDidStorageChange(): void {
|
||||
this.trustStateInfo = this.loadTrustInfo();
|
||||
|
||||
this._onDidChangeTrustState.fire();
|
||||
}
|
||||
|
||||
setFolderTrustState(folder: URI, trustState: WorkspaceTrustState): void {
|
||||
let changed = false;
|
||||
|
||||
if (trustState === WorkspaceTrustState.Unknown) {
|
||||
const before = this.trustStateInfo.localFolders.length;
|
||||
this.trustStateInfo.localFolders = this.trustStateInfo.localFolders.filter(info => info.uri !== folder.toString());
|
||||
|
||||
if (this.trustStateInfo.localFolders.length !== before) {
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
let found = false;
|
||||
for (const trustInfo of this.trustStateInfo.localFolders) {
|
||||
if (trustInfo.uri === folder.toString()) {
|
||||
found = true;
|
||||
if (trustInfo.trustState !== trustState) {
|
||||
trustInfo.trustState = trustState;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
this.trustStateInfo.localFolders.push({ uri: folder.toString(), trustState });
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.saveTrustInfo();
|
||||
}
|
||||
}
|
||||
|
||||
getFolderTrustState(folder: URI): WorkspaceTrustState {
|
||||
for (const trustInfo of this.trustStateInfo.localFolders) {
|
||||
if (trustInfo.uri === folder.toString()) {
|
||||
return trustInfo.trustState;
|
||||
}
|
||||
}
|
||||
|
||||
return WorkspaceTrustState.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkspaceTrustRequestModel extends Disposable implements IWorkspaceTrustRequestModel {
|
||||
trustRequest: IWorkspaceTrustRequest | undefined;
|
||||
|
||||
private readonly _onDidInitiateRequest = this._register(new Emitter<void>());
|
||||
readonly onDidInitiateRequest: Event<void> = this._onDidInitiateRequest.event;
|
||||
|
||||
private readonly _onDidCompleteRequest = this._register(new Emitter<WorkspaceTrustState | undefined>());
|
||||
readonly onDidCompleteRequest = this._onDidCompleteRequest.event;
|
||||
|
||||
initiateRequest(request: IWorkspaceTrustRequest): void {
|
||||
if (this.trustRequest && (!request.immediate || this.trustRequest.immediate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.trustRequest = request;
|
||||
this._onDidInitiateRequest.fire();
|
||||
}
|
||||
|
||||
completeRequest(trustState?: WorkspaceTrustState): void {
|
||||
this.trustRequest = undefined;
|
||||
this._onDidCompleteRequest.fire(trustState);
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkspaceTrustService extends Disposable implements IWorkspaceTrustService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
private readonly dataModel: IWorkspaceTrustModel;
|
||||
readonly requestModel: IWorkspaceTrustRequestModel;
|
||||
|
||||
private readonly _onDidChangeTrustState = this._register(new Emitter<WorkspaceTrustStateChangeEvent>());
|
||||
readonly onDidChangeTrustState = this._onDidChangeTrustState.event;
|
||||
|
||||
private _currentTrustState: WorkspaceTrustState = WorkspaceTrustState.Unknown;
|
||||
private _inFlightResolver?: (trustState: WorkspaceTrustState) => void;
|
||||
private _trustRequestPromise?: Promise<WorkspaceTrustState>;
|
||||
private _workspace: IWorkspace;
|
||||
|
||||
private readonly _ctxWorkspaceTrustState: IContextKey<WorkspaceTrustState>;
|
||||
private readonly _ctxWorkspaceTrustPendingRequest: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService readonly configurationService: IConfigurationService,
|
||||
@IContextKeyService readonly contextKeyService: IContextKeyService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.dataModel = this._register(new WorkspaceTrustModel(this.storageService));
|
||||
this.requestModel = this._register(new WorkspaceTrustRequestModel());
|
||||
|
||||
this._workspace = this.workspaceService.getWorkspace();
|
||||
this._currentTrustState = this.calculateWorkspaceTrustState();
|
||||
|
||||
this._register(this.dataModel.onDidChangeTrustState(() => this.currentTrustState = this.calculateWorkspaceTrustState()));
|
||||
this._register(this.requestModel.onDidCompleteRequest((trustState) => this.onTrustRequestCompleted(trustState)));
|
||||
|
||||
this._ctxWorkspaceTrustState = WorkspaceTrustContext.TrustState.bindTo(contextKeyService);
|
||||
this._ctxWorkspaceTrustPendingRequest = WorkspaceTrustContext.PendingRequest.bindTo(contextKeyService);
|
||||
this._ctxWorkspaceTrustState.set(this.currentTrustState);
|
||||
}
|
||||
|
||||
private get currentTrustState(): WorkspaceTrustState {
|
||||
return this._currentTrustState;
|
||||
}
|
||||
|
||||
private set currentTrustState(trustState: WorkspaceTrustState) {
|
||||
if (this._currentTrustState === trustState) { return; }
|
||||
const previousState = this._currentTrustState;
|
||||
this._currentTrustState = trustState;
|
||||
|
||||
this._onDidChangeTrustState.fire({ previousTrustState: previousState, currentTrustState: this._currentTrustState });
|
||||
}
|
||||
|
||||
private calculateWorkspaceTrustState(): WorkspaceTrustState {
|
||||
if (!this.isWorkspaceTrustEnabled()) {
|
||||
return WorkspaceTrustState.Trusted;
|
||||
}
|
||||
|
||||
if (this.workspaceService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
return WorkspaceTrustState.Trusted;
|
||||
}
|
||||
|
||||
let state = undefined;
|
||||
for (const folder of this._workspace.folders) {
|
||||
const folderTrust = this.dataModel.getFolderTrustState(folder.uri);
|
||||
|
||||
switch (folderTrust) {
|
||||
case WorkspaceTrustState.Untrusted:
|
||||
return WorkspaceTrustState.Untrusted;
|
||||
case WorkspaceTrustState.Unknown:
|
||||
state = folderTrust;
|
||||
break;
|
||||
case WorkspaceTrustState.Trusted:
|
||||
if (state === undefined) {
|
||||
state = folderTrust;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return state ?? WorkspaceTrustState.Unknown;
|
||||
}
|
||||
|
||||
private onTrustRequestCompleted(trustState?: WorkspaceTrustState): void {
|
||||
if (this._inFlightResolver) {
|
||||
this._inFlightResolver(trustState === undefined ? this.currentTrustState : trustState);
|
||||
}
|
||||
|
||||
this._inFlightResolver = undefined;
|
||||
this._trustRequestPromise = undefined;
|
||||
|
||||
if (trustState === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._workspace.folders.forEach(folder => {
|
||||
this.dataModel.setFolderTrustState(folder.uri, trustState);
|
||||
});
|
||||
|
||||
this._ctxWorkspaceTrustPendingRequest.set(false);
|
||||
this._ctxWorkspaceTrustState.set(trustState);
|
||||
}
|
||||
|
||||
getWorkspaceTrustState(): WorkspaceTrustState {
|
||||
return this.currentTrustState;
|
||||
}
|
||||
|
||||
isWorkspaceTrustEnabled(): boolean {
|
||||
return this.configurationService.getValue<boolean>(WORKSPACE_TRUST_ENABLED) ?? false;
|
||||
}
|
||||
|
||||
async requireWorkspaceTrust(request?: IWorkspaceTrustRequest): Promise<WorkspaceTrustState> {
|
||||
if (this.currentTrustState === WorkspaceTrustState.Trusted) {
|
||||
return this.currentTrustState;
|
||||
}
|
||||
if (this.currentTrustState === WorkspaceTrustState.Untrusted && !request?.immediate) {
|
||||
return this.currentTrustState;
|
||||
}
|
||||
|
||||
if (this._trustRequestPromise) {
|
||||
if (request?.immediate &&
|
||||
this.requestModel.trustRequest &&
|
||||
!this.requestModel.trustRequest.immediate) {
|
||||
this.requestModel.initiateRequest(request);
|
||||
}
|
||||
|
||||
return this._trustRequestPromise;
|
||||
}
|
||||
|
||||
this._trustRequestPromise = new Promise(resolve => {
|
||||
this._inFlightResolver = resolve;
|
||||
});
|
||||
|
||||
this.requestModel.initiateRequest(request);
|
||||
this._ctxWorkspaceTrustPendingRequest.set(true);
|
||||
|
||||
return this._trustRequestPromise;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IWorkspaceTrustService, WorkspaceTrustService);
|
|
@ -4,7 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWorkspaceTrustRequest, IWorkspaceTrustRequestModel, IWorkspaceTrustService, WorkspaceTrustChangeEvent, WorkspaceTrustRequestModel, WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { IWorkspaceTrustRequest, IWorkspaceTrustRequestModel, IWorkspaceTrustService, WorkspaceTrustChangeEvent, WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { WorkspaceTrustRequestModel } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
|
||||
export class TestWorkspaceTrustService implements IWorkspaceTrustService {
|
||||
_serviceBrand: undefined;
|
||||
|
@ -24,8 +25,4 @@ export class TestWorkspaceTrustService implements IWorkspaceTrustService {
|
|||
requireWorkspaceTrust(request: IWorkspaceTrustRequest): Promise<WorkspaceTrustState> {
|
||||
return Promise.resolve(WorkspaceTrustState.Trusted);
|
||||
}
|
||||
|
||||
resetWorkspaceTrust(): Promise<WorkspaceTrustState> {
|
||||
return Promise.resolve(WorkspaceTrustState.Unknown);
|
||||
}
|
||||
}
|
|
@ -125,7 +125,7 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u
|
|||
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
|
||||
import { IEnterWorkspaceResult, IRecent, IRecentlyOpened, IWorkspaceFolderCreationData, IWorkspaceIdentifier, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspaceTrustService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { TestWorkspaceTrustService } from 'vs/platform/workspace/test/common/testWorkspaceTrust';
|
||||
import { TestWorkspaceTrustService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService';
|
||||
|
||||
export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
|
||||
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined);
|
||||
|
|
|
@ -10074,10 +10074,10 @@ vm-browserify@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
|
||||
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
|
||||
|
||||
vscode-debugprotocol@1.44.0:
|
||||
version "1.44.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.44.0.tgz#79d11844f908cc5104afc303b3780ad2e5fd486a"
|
||||
integrity sha512-qf+eBnrDyR2MpP08y1JfzJnFZGHdkk86+SRGRp0XepDGNA6n/Nann5XhtAzdGX/yaZokjTAINK313S2yYhHoPQ==
|
||||
vscode-debugprotocol@1.45.0-pre.0:
|
||||
version "1.45.0-pre.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.45.0-pre.0.tgz#7ea4c6e84d966e96a8398241e46bdf98e0dacfde"
|
||||
integrity sha512-q0ivFfnjFQyL4Qyxp1K8gSdTpfcvlCxmys5adLydBef1XvDKZNONAKalCZZCyaG/vtOlbV1ma6hS5eALNaLd8w==
|
||||
|
||||
vscode-nls-dev@^3.3.1:
|
||||
version "3.3.1"
|
||||
|
|
Loading…
Reference in a new issue