Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build/gulpfile.vscode.win32.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ function buildWin32Setup(arch: string, target: string): task.CallbackTask {
definitions['ProxyExeBasename'] = embedded.nameShort;
definitions['ProxyAppUserId'] = embedded.win32AppUserModelId;
definitions['ProxyNameLong'] = embedded.nameLong;
definitions['ProxyExeUrlProtocol'] = embedded.urlProtocol;
}

if (quality === 'stable' || quality === 'insider') {
Expand Down
9 changes: 9 additions & 0 deletions build/win32/code.iss
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,15 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValu
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Check: ShouldInstallLegacyFolderContextMenu
Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Check: ShouldInstallLegacyFolderContextMenu

; URL Protocol handler for proxy executable
#ifdef ProxyExeBasename
#ifdef ProxyExeUrlProtocol
Root: HKCU; Subkey: "Software\Classes\{#ProxyExeUrlProtocol}"; ValueType: string; ValueName: ""; ValueData: "URL:{#ProxyExeUrlProtocol}"; Flags: uninsdeletekey
Root: HKCU; Subkey: "Software\Classes\{#ProxyExeUrlProtocol}"; ValueType: string; ValueName: "URL Protocol"; ValueData: ""; Flags: uninsdeletekey
Root: HKCU; Subkey: "Software\Classes\{#ProxyExeUrlProtocol}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ProxyExeBasename}.exe"" --open-url -- ""%1"""; Flags: uninsdeletekey
#endif
#endif

; Environment
#if "user" == InstallTarget
#define EnvironmentRootKey "HKCU"
Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/common/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ export class ExtUri implements IExtUri {
return basename(resource) || resource.authority;
}

basename(resource: URI): string {
return paths.posix.basename(resource.path);
basename(resource: URI, suffix?: string): string {
return paths.posix.basename(resource.path, suffix);
}

extname(resource: URI): string {
Expand Down
26 changes: 22 additions & 4 deletions src/vs/code/electron-main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -743,13 +743,20 @@ export class CodeApplication extends Disposable {

const openables: IWindowOpenable[] = [];
const urls: IProtocolUrl[] = [];

for (const protocolUrl of protocolUrls) {
if (!protocolUrl) {
continue; // invalid
}

const windowOpenable = this.getWindowOpenableFromProtocolUrl(protocolUrl.uri);
if (windowOpenable) {
// Sessions app: skip all window openables (file/folder/workspace)
if ((process as INodeProcess).isEmbeddedApp) {
this.logService.trace('app#resolveInitialProtocolUrls() sessions app skipping window openable:', protocolUrl.uri.toString(true));
continue;
}

if (await this.shouldBlockOpenable(windowOpenable, windowsMainService, dialogMainService)) {
this.logService.trace('app#resolveInitialProtocolUrls() protocol url was blocked:', protocolUrl.uri.toString(true));

Expand Down Expand Up @@ -895,13 +902,24 @@ export class CodeApplication extends Disposable {
private async handleProtocolUrl(windowsMainService: IWindowsMainService, dialogMainService: IDialogMainService, urlService: IURLService, uri: URI, options?: IOpenURLOptions): Promise<boolean> {
this.logService.trace('app#handleProtocolUrl():', uri.toString(true), options);

// Sessions app: "open a sessions window", regardless of other parameters.
// Sessions app: ensure the sessions window is open, then let other handlers process the URL.
if ((process as INodeProcess).isEmbeddedApp) {
this.logService.trace('app#handleProtocolUrl() opening sessions window for bare protocol URL:', uri.toString(true));
this.logService.trace('app#handleProtocolUrl() sessions app handling protocol URL:', uri.toString(true));

await windowsMainService.openSessionsWindow({ context: OpenContext.LINK, contextWindowId: undefined });
// Skip window openables (file/folder/workspace) for security
const windowOpenable = this.getWindowOpenableFromProtocolUrl(uri);
if (windowOpenable) {
this.logService.trace('app#handleProtocolUrl() sessions app skipping window openable:', uri.toString(true));
return true;
}

// Ensure sessions window is open to receive the URL
const windows = await windowsMainService.openSessionsWindow({ context: OpenContext.LINK, contextWindowId: undefined });
const window = windows.at(0);
await window?.ready();

return true;
// Return false to let subsequent handlers (e.g., URLHandlerChannelClient) forward the URL
return false;
}

// Support 'workspace' URLs (https://github.com/microsoft/vscode/issues/124263)
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/environment/common/argv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export interface NativeParsedArgs {
'disable-telemetry'?: boolean;
'export-default-configuration'?: string;
'export-policy-data'?: string;
'export-default-keybindings'?: string;
'install-source'?: string;
'add-mcp'?: string[];
'disable-updates'?: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/environment/common/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export interface INativeEnvironmentService extends IEnvironmentService {

crossOriginIsolated?: boolean;
exportPolicyData?: string;
exportDefaultKeybindings?: string;

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
Expand Down
4 changes: 4 additions & 0 deletions src/vs/platform/environment/common/environmentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
return this.args['export-policy-data'];
}

get exportDefaultKeybindings(): string | undefined {
return this.args['export-default-keybindings'];
}

get continueOn(): string | undefined {
return this.args['continueOn'];
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/environment/node/argv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
'inspect-brk-sharedprocess': { type: 'string', allowEmptyValue: true },
'export-default-configuration': { type: 'string' },
'export-policy-data': { type: 'string', allowEmptyValue: true },
'export-default-keybindings': { type: 'string', allowEmptyValue: true },
'install-source': { type: 'string' },
'enable-smoke-test-driver': { type: 'boolean' },
'skip-sessions-welcome': { type: 'boolean' },
Expand Down
69 changes: 62 additions & 7 deletions src/vs/platform/keybinding/common/keybindingsRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export interface IKeybindingsRegistry {
setExtensionKeybindings(rules: IExtensionKeybindingRule[]): void;
registerCommandAndKeybindingRule<Args extends unknown[] = unknown[]>(desc: ICommandAndKeybindingRule<Args>): IDisposable;
getDefaultKeybindings(): IKeybindingItem[];
getDefaultKeybindingsForOS(os: OperatingSystem): IKeybindingItem[];
}

/**
Expand All @@ -85,24 +86,23 @@ export interface IKeybindingsRegistry {
class KeybindingsRegistryImpl implements IKeybindingsRegistry {

private _coreKeybindings: LinkedList<IKeybindingItem>;
private _coreKeybindingRules: LinkedList<IKeybindingRule>;
private _extensionKeybindings: IKeybindingItem[];
private _cachedMergedKeybindings: IKeybindingItem[] | null;

constructor() {
this._coreKeybindings = new LinkedList();
this._coreKeybindingRules = new LinkedList();
this._extensionKeybindings = [];
this._cachedMergedKeybindings = null;
}

/**
* Take current platform into account and reduce to primary & secondary.
*/
private static bindToCurrentPlatform(kb: IKeybindings): { primary?: number; secondary?: number[] } {
if (OS === OperatingSystem.Windows) {
private static bindToPlatform(kb: IKeybindings, os: OperatingSystem): { primary?: number; secondary?: number[] } {
if (os === OperatingSystem.Windows) {
if (kb && kb.win) {
return kb.win;
}
} else if (OS === OperatingSystem.Macintosh) {
} else if (os === OperatingSystem.Macintosh) {
if (kb && kb.mac) {
return kb.mac;
}
Expand All @@ -111,10 +111,16 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
return kb.linux;
}
}

return kb;
}

/**
* Take current platform into account and reduce to primary & secondary.
*/
private static bindToCurrentPlatform(kb: IKeybindings): { primary?: number; secondary?: number[] } {
return KeybindingsRegistryImpl.bindToPlatform(kb, OS);
}

public registerKeybindingRule(rule: IKeybindingRule): IDisposable {
const actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform(rule);
const result = new DisposableStore();
Expand All @@ -135,6 +141,10 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
}
}
}

const removeRule = this._coreKeybindingRules.push(rule);
result.add(toDisposable(() => { removeRule(); }));

return result;
}

Expand Down Expand Up @@ -193,6 +203,51 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry {
}
return this._cachedMergedKeybindings.slice(0);
}

public getDefaultKeybindingsForOS(os: OperatingSystem): IKeybindingItem[] {
const result: IKeybindingItem[] = [];
for (const rule of this._coreKeybindingRules) {
const actualKb = KeybindingsRegistryImpl.bindToPlatform(rule, os);

if (actualKb && actualKb.primary) {
const kk = decodeKeybinding(actualKb.primary, os);
if (kk) {
result.push({
keybinding: kk,
command: rule.id,
commandArgs: rule.args,
when: rule.when,
weight1: rule.weight,
weight2: 0,
extensionId: null,
isBuiltinExtension: false
});
}
}

if (actualKb && Array.isArray(actualKb.secondary)) {
for (let i = 0, len = actualKb.secondary.length; i < len; i++) {
const k = actualKb.secondary[i];
const kk = decodeKeybinding(k, os);
if (kk) {
result.push({
keybinding: kk,
command: rule.id,
commandArgs: rule.args,
when: rule.when,
weight1: rule.weight,
weight2: -i - 1,
extensionId: null,
isBuiltinExtension: false
});
}
}
}
}

result.sort(sorter);
return result;
}
}
export const KeybindingsRegistry: IKeybindingsRegistry = new KeybindingsRegistryImpl();

Expand Down
7 changes: 4 additions & 3 deletions src/vs/platform/url/electron-main/electronUrlListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { app, Event as ElectronEvent } from 'electron';
import { disposableTimeout } from '../../../base/common/async.js';
import { Event } from '../../../base/common/event.js';
import { Disposable } from '../../../base/common/lifecycle.js';
import { isWindows } from '../../../base/common/platform.js';
import { INodeProcess, isWindows } from '../../../base/common/platform.js';
import { URI } from '../../../base/common/uri.js';
import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js';
import { ILogService } from '../../log/common/log.js';
Expand Down Expand Up @@ -50,8 +50,9 @@ export class ElectronURLListener extends Disposable {

// Windows: install as protocol handler
// Skip in portable mode: the registered command wouldn't preserve
// portable mode settings, causing issues with OAuth flows
if (isWindows && !environmentMainService.isPortable) {
// portable mode settings, causing issues with OAuth flows.
// Skip for embedded apps: protocol handler is registered at install time.
if (isWindows && !environmentMainService.isPortable && !(process as INodeProcess).isEmbeddedApp) {
const windowsParameters = environmentMainService.isBuilt ? [] : [`"${environmentMainService.appRoot}"`];
windowsParameters.push('--open-url', '--');
app.setAsDefaultProtocolClient(productService.urlProtocol, process.execPath, windowsParameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,78 @@
import './agentFeedbackEditorInputContribution.js';
import './agentFeedbackEditorWidgetContribution.js';
import './agentFeedbackOverviewRulerContribution.js';
import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
import { autorun, observableFromEvent } from '../../../../base/common/observable.js';
import { localize } from '../../../../nls.js';
import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js';
import { IContextKeyService, ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';
import { IsSessionsWindowContext } from '../../../../workbench/common/contextkeys.js';
import { AgentFeedbackService, IAgentFeedbackService } from './agentFeedbackService.js';
import { AgentFeedbackAttachmentContribution } from './agentFeedbackAttachment.js';
import { AgentFeedbackAttachmentWidget } from './agentFeedbackAttachmentWidget.js';
import { AgentFeedbackEditorOverlay } from './agentFeedbackEditorOverlay.js';
import { registerAgentFeedbackEditorActions } from './agentFeedbackEditorActions.js';
import { hasActiveSessionAgentFeedback, registerAgentFeedbackEditorActions, submitActiveSessionFeedbackActionId } from './agentFeedbackEditorActions.js';
import { IChatAttachmentWidgetRegistry } from '../../../../workbench/contrib/chat/browser/attachments/chatAttachmentWidgetRegistry.js';
import { IAgentFeedbackVariableEntry } from '../../../../workbench/contrib/chat/common/attachments/chatVariableEntries.js';
import { ISessionsManagementService } from '../../sessions/browser/sessionsManagementService.js';
import { Codicon } from '../../../../base/common/codicons.js';

/**
* Sets the `hasActiveSessionAgentFeedback` context key to true when the
* currently active session has pending agent feedback items.
*/
class ActiveSessionFeedbackContextContribution extends Disposable implements IWorkbenchContribution {

static readonly ID = 'workbench.contrib.activeSessionFeedbackContext';

constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@IAgentFeedbackService agentFeedbackService: IAgentFeedbackService,
@ISessionsManagementService sessionManagementService: ISessionsManagementService,
) {
super();

const contextKey = hasActiveSessionAgentFeedback.bindTo(contextKeyService);
const menuRegistration = this._register(new MutableDisposable());

const feedbackChanged = observableFromEvent(
this,
agentFeedbackService.onDidChangeFeedback,
e => e,
);

this._register(autorun(reader => {
feedbackChanged.read(reader);
const activeSession = sessionManagementService.activeSession.read(reader);
menuRegistration.clear();
if (!activeSession) {
contextKey.set(false);
return;
}
const feedback = agentFeedbackService.getFeedback(activeSession.resource);
const count = feedback.length;
contextKey.set(count > 0);

if (count > 0) {
menuRegistration.value = MenuRegistry.appendMenuItem(MenuId.ChatEditingSessionApplySubmenu, {
command: {
id: submitActiveSessionFeedbackActionId,
icon: Codicon.comment,
title: localize('agentFeedback.submitFeedbackCount', "Submit Feedback ({0})", count),
},
group: 'navigation',
order: 3,
when: ContextKeyExpr.and(IsSessionsWindowContext, hasActiveSessionAgentFeedback),
});
}
}));
}
}

registerWorkbenchContribution2(ActiveSessionFeedbackContextContribution.ID, ActiveSessionFeedbackContextContribution, WorkbenchPhase.AfterRestored);
registerWorkbenchContribution2(AgentFeedbackEditorOverlay.ID, AgentFeedbackEditorOverlay, WorkbenchPhase.AfterRestored);
registerWorkbenchContribution2(AgentFeedbackAttachmentContribution.ID, AgentFeedbackAttachmentContribution, WorkbenchPhase.AfterRestored);

Expand Down
Loading
Loading