Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 8, 2025

Description

"Select Default Profile" had no effect in workspaces with custom terminal settings because it only updated user settings, which workspace settings override.

Changes

Architectural Refactoring

Centralized Profile Scope Detection (addresses review feedback):

  • Added configScope property to ITerminalProfile interface to track configuration origin (USER, WORKSPACE, or undefined for auto-detected profiles)
  • Modified detectAvailableProfiles in terminalProfiles.ts to inspect configuration once and set configScope for each profile
  • Eliminates duplicate profiles in the picker by separating user and workspace profiles into distinct sections based on configScope
  • Provides single source of truth for profile scope detection, enabling future features to use this information without reimplementing detection logic

Target Selection

When workspace and user default profiles differ, show a second picker to choose where to apply:

  • User: Updates user settings only (workspace override remains)
  • Workspace: Updates workspace settings only
  • Both: Updates both user and workspace settings

Implementation in showAndGetResult():

// Detect conflict
const configInspect = this._configurationService.inspect(defaultProfileKey);
if (workspaceDefault !== undefined && workspaceDefault !== userDefault) {
    const targetChoice = await this._showTargetPicker();
    targets = targetChoice; // [USER] | [WORKSPACE] | [USER, WORKSPACE]
}

// Apply to selected targets
for (const target of targets) {
    await this._configurationService.updateValue(defaultProfileKey, profileName, target);
}

Workspace Profiles Section

Added "workspace" separator in profile picker showing workspace-defined profiles. Profiles are separated based on their configScope property:

  • User and auto-detected profiles appear in the "profiles" section
  • Workspace-specific profiles appear in the "workspace" section
  • Eliminates duplicate profiles that previously appeared in both sections

Scope-Aware Configuration

Profile updates now read from and write to the appropriate scope (user vs workspace) using inspect() instead of getValue() to avoid merging configurations across scopes.

Benefits

  • Single source of truth: Profile scope detection happens once in the platform layer (detectAvailableProfiles)
  • No duplicate profiles: Each profile appears in only one section based on its configScope
  • Better architecture: Separation of concerns - platform layer detects scope, UI layer uses it
  • Future-proof: Other features can rely on configScope without reimplementing detection logic

Backward Compatibility

  • No workspace settings: Behavior unchanged (user settings only)
  • Workspace matches user: Behavior unchanged (user settings only)
  • New UI only appears when workspace overrides user default
Original prompt

This section details on the original issue you should resolve

<issue_title>Select Default Profile (terminal) doesn't work for projects with terminal specified</issue_title>
<issue_description>
Type: Bug

  1. Open VScode

  2. Open Terminal
    See zsh is the default terminal
    Image

  3. Clone https://github.com/valkey-io/valkey-search/

  4. Open cloned repository in vscode

  5. Open Terminal
    See bash is the default terminal
    Image

  6. Use the drop down next to the plus next to bash

  7. Select Select Default Profile
    Image

  8. Select zsh -l

  9. Close and reopen vscode

  10. Open Terminal
    See bash is still the default terminal
    Image

The problem is that the Select Default Profile setting doesn't change the project specific file and nothing else in the menu appears to let you:
https://github.com/valkey-io/valkey-search/blob/main/.vscode/settings.json#L3-L4

Preferably, the UI should edit the project settings, but if it doesn't, it should warn users that the setting is useless for the project and point to the file they can hand edit.

VS Code version: Code 1.100.3 (Universal) (258e40f, 2025-06-02T13:30:54.273Z)
OS version: Darwin arm64 24.5.0
Modes:

System Info
Item Value
CPUs Apple M1 Max (10 x 2400)
GPU Status 2d_canvas: enabled
canvas_oop_rasterization: enabled_on
direct_rendering_display_compositor: disabled_off_ok
gpu_compositing: enabled
multiple_raster_threads: enabled_on
opengl: enabled_on
rasterization: enabled
raw_draw: disabled_off_ok
skia_graphite: disabled_off
video_decode: enabled
video_encode: enabled
webgl: enabled
webgl2: enabled
webgpu: enabled
webnn: disabled_off
Load (avg) 3, 4, 9
Memory (System) 64.00GB (30.64GB free)
Process Argv valkey-search --crash-reporter-id 1fc67ee2-0174-4598-9f98-4537df0dd32c
Screen Reader no
VM 0%
Extensions (23)
Extension Author (truncated) Version
quitcontrol-vscode art 4.0.0
asciidoctor-vscode asc 3.4.2
yamlfmt blu 0.1.6
vscode-intelephense-client bme 1.14.4
open-in-macdown Cod 1.0.0
intelli-php-vscode DEV 0.12.15062
dhall-lang dha 0.0.4
vscode-dhall-lsp-server dha 0.0.4
docker doc 0.9.0
EditorConfig Edi 0.17.4
html-preview-vscode geo 0.2.5
vscode-github-actions git 0.27.2
vscode-pull-request-github Git 0.111.2025060504
go gol 0.46.1
file-downloader min 1.0.13
vscode-containers ms- 2.0.3
vscode-kubernetes-tools ms- 1.3.23
sarif-viewer MS- 3.4.5
vscode-dhall-lsp-server pan 0.0.4
vscode-xml red 0.29.0
vscode-yaml red 1.18.0
rst-vscode tht 3.0.1
simple-rst tro 1.5.4
A/B Experiments
vsliv368:30146709
vspor879:30202332
vspor708:30202333
vspor363:30204092
vswsl492:30256859
vscod805cf:30301675
binariesv615:30325510
c4g48928:30535728
azure-dev_surveyone:30548225
vscrp:30673768
962ge761:30959799
h48ei257:31000450
pythontbext0:30879054
cppperfnew:31000557
dwnewjupytercf:31046870
pythonrstrctxt:31112756
nativeloc2:31192216
5fd0e150:31155592
dwcopilot:31170013
6074i472:31201624
dwoutputs:31242946
customenabled:31248079
hdaa2157:31222309
copilot_t_ci:31222730
e5gg6876:31282496
pythoneinst12:31285622
bgtreat:31268568
4gafe986:31271826
c7cif404:31314491
pythonpulldiagcf:31316724
996jf627:31283433
pythonrdcb7cf:31303019
usemplatestapi:31297334
0aa6g176:31307128
7bj51361:31289155
747dc170:31275177
pylancecolor:31314202
aj953862:31281341
generatesymbolt:31295002
convertfstringf:31295003
gendocf:31295004

<agent_instructions>If the workspace has a defaultProfile different from the one defined for the user, add a second step to the picker that allows choosing whether the value will apply to the User, Workspace, both or neither.
Add a new section with title workspace that displays any terminal.integrated.profiles defined for the workspace</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@meganrogge I cannot repro, probably has been fixed, thus closing. @meganrogge Yep, tried with those lines. In fact, that's what I have had as my default shell for several months. @meganrogge Why would a specific repo be needed here? @meganrogge Oh i see- you're defining that s...

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits December 8, 2025 19:53
Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix default terminal profile selection in VSCode Fix terminal default profile selection for workspaces with custom profiles Dec 8, 2025
Copilot AI requested a review from meganrogge December 8, 2025 20:04
@meganrogge meganrogge requested a review from Tyriar December 8, 2025 21:55
@meganrogge meganrogge added this to the December / January 2026 milestone Dec 8, 2025
@meganrogge meganrogge marked this pull request as ready for review December 8, 2025 21:55
Copilot AI review requested due to automatic review settings December 8, 2025 21:55
@meganrogge meganrogge enabled auto-merge (squash) December 8, 2025 21:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where "Select Default Profile" had no effect in workspaces with custom terminal settings. The issue was that the command only updated user settings, which were overridden by workspace settings.

Key Changes

  • Target selection logic: When workspace and user default profiles differ, a second picker allows users to choose whether to update User, Workspace, or Both settings
  • Workspace profiles section: Added a "workspace" separator in the profile picker to show workspace-defined profiles
  • Scope-aware configuration: Profile updates now use inspect() to read from and write to specific scopes (user vs workspace) instead of merging configurations

Comment on lines 203 to 215
// Add workspace profiles section if workspace has any profiles defined
const workspaceProfilesConfig = this._configurationService.inspect(profilesKey);
if (workspaceProfilesConfig.workspaceValue && typeof workspaceProfilesConfig.workspaceValue === 'object') {
const workspaceProfiles = Object.keys(workspaceProfilesConfig.workspaceValue as { [key: string]: unknown });
if (workspaceProfiles.length > 0) {
quickPickItems.push({ type: 'separator', label: nls.localize('terminalProfiles.workspace', "workspace") });
// Filter available profiles to only include workspace-defined ones
const workspaceProfileItems = profiles
.filter(e => workspaceProfiles.includes(e.profileName))
.map(e => this._createProfileQuickPickItem(e));
quickPickItems.push(...this._sortProfileQuickPickItems(workspaceProfileItems, defaultProfileName!));
}
}
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workspace profiles section (lines 203-215) lacks test coverage. Consider adding tests that verify:

  1. Workspace profiles are displayed in a separate section when workspace has profile definitions
  2. The section is not shown when workspace has no profile definitions
  3. Workspace profiles are correctly filtered and sorted

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +85
for (const target of targets) {
if (hasKey(result.profile, { id: true })) {
// extension contributed profile
await this._configurationService.updateValue(defaultProfileKey, result.profile.title, target);
} else {
// Add the profile to settings if necessary
if (hasKey(result.profile, { profileName: true })) {
// Get the appropriate configuration based on target
const profilesInspect = this._configurationService.inspect(profilesKey);
let profilesConfig: { [key: string]: ITerminalProfileObject };
if (target === ConfigurationTarget.WORKSPACE) {
profilesConfig = profilesInspect.workspaceValue || {};
} else {
profilesConfig = profilesInspect.userValue || {};
}

if (typeof profilesConfig === 'object') {
const newProfilesConfig = { ...profilesConfig };
newProfilesConfig[result.profile.profileName] = this._createNewProfileConfig(result.profile);
await this._configurationService.updateValue(profilesKey, newProfilesConfig, target);
}
}
// Set the default profile
await this._configurationService.updateValue(defaultProfileKey, result.profileName, target);
}
}
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The configuration update loop (lines 60-85) contains nested conditional logic that handles two different code paths (extension profiles vs. regular profiles). Consider extracting this into a separate private method like _updateProfileConfiguration(target: ConfigurationTarget, result: IProfileQuickPickItem, profilesKey: string, defaultProfileKey: string) to improve readability and maintainability. This would make the main flow clearer and make the logic easier to test.

Copilot uses AI. Check for mistakes.
Comment on lines 203 to 215
// Add workspace profiles section if workspace has any profiles defined
const workspaceProfilesConfig = this._configurationService.inspect(profilesKey);
if (workspaceProfilesConfig.workspaceValue && typeof workspaceProfilesConfig.workspaceValue === 'object') {
const workspaceProfiles = Object.keys(workspaceProfilesConfig.workspaceValue as { [key: string]: unknown });
if (workspaceProfiles.length > 0) {
quickPickItems.push({ type: 'separator', label: nls.localize('terminalProfiles.workspace', "workspace") });
// Filter available profiles to only include workspace-defined ones
const workspaceProfileItems = profiles
.filter(e => workspaceProfiles.includes(e.profileName))
.map(e => this._createProfileQuickPickItem(e));
quickPickItems.push(...this._sortProfileQuickPickItems(workspaceProfileItems, defaultProfileName!));
}
}
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workspace profiles section may show duplicate profiles. This code filters profiles to only include workspace-defined ones (lines 210-212), but these profiles may already be shown in the "profiles" section above (lines 198-201). Since profiles includes all available profiles (both user and workspace), and workspace profiles are typically also in the merged profiles list, this will result in the same profiles appearing twice in the picker - once in the "profiles" section and once in the "workspace" section. Consider checking if a profile is already included before adding it to the workspace section, or filter the "profiles" section to exclude workspace-only profiles.

Copilot uses AI. Check for mistakes.
Comment on lines 44 to 57
// Check if workspace has a different default profile than user
const configInspect = this._configurationService.inspect(defaultProfileKey);
const userDefault = configInspect.userValue;
const workspaceDefault = configInspect.workspaceValue;

// If workspace and user defaults differ, ask where to apply the change
let targets: ConfigurationTarget[] = [ConfigurationTarget.USER];
if (workspaceDefault !== undefined && workspaceDefault !== userDefault) {
const targetChoice = await this._showTargetPicker();
if (!targetChoice) {
return; // User cancelled
}
targets = targetChoice;
}
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new workspace vs. user configuration target selection logic (lines 44-57) lacks test coverage. The existing tests in terminalProfileService.integrationTest.ts only test the basic setDefault functionality without workspace settings. Consider adding tests that verify:

  1. The target picker is shown when workspace and user defaults differ
  2. The target picker is not shown when they match or when workspace is undefined
  3. Configuration is correctly applied to the selected targets (USER, WORKSPACE, or both)
  4. User cancellation is handled correctly

Copilot uses AI. Check for mistakes.
Comment on lines 203 to 215
// Add workspace profiles section if workspace has any profiles defined
const workspaceProfilesConfig = this._configurationService.inspect(profilesKey);
if (workspaceProfilesConfig.workspaceValue && typeof workspaceProfilesConfig.workspaceValue === 'object') {
const workspaceProfiles = Object.keys(workspaceProfilesConfig.workspaceValue as { [key: string]: unknown });
if (workspaceProfiles.length > 0) {
quickPickItems.push({ type: 'separator', label: nls.localize('terminalProfiles.workspace', "workspace") });
// Filter available profiles to only include workspace-defined ones
const workspaceProfileItems = profiles
.filter(e => workspaceProfiles.includes(e.profileName))
.map(e => this._createProfileQuickPickItem(e));
quickPickItems.push(...this._sortProfileQuickPickItems(workspaceProfileItems, defaultProfileName!));
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot this should do the inspect in detectAvailableProfiles in terminalProfiles.ts and then pass that info back from the profile via ITerminalProfile.configScope:

export function detectAvailableProfiles(
profiles: unknown,
defaultProfile: unknown,
includeDetectedProfiles: boolean,
configurationService: IConfigurationService,
shellEnv: typeof process.env = process.env,
fsProvider?: IFsProvider,
logService?: ILogService,
variableResolver?: (text: string[]) => Promise<string[]>,
testPwshSourcePaths?: string[]
): Promise<ITerminalProfile[]> {
fsProvider = fsProvider || {
existsFile: pfs.SymlinkSupport.existsFile,
readFile: fs.promises.readFile
};
if (isWindows) {
return detectAvailableWindowsProfiles(
includeDetectedProfiles,
fsProvider,
shellEnv,
logService,
configurationService.getValue(TerminalSettingId.UseWslProfiles) !== false,
profiles && isObject(profiles) ? { ...profiles } : configurationService.getValue<{ [key: string]: IUnresolvedTerminalProfile }>(TerminalSettingId.ProfilesWindows),
isString(defaultProfile) ? defaultProfile : configurationService.getValue<string>(TerminalSettingId.DefaultProfileWindows),
testPwshSourcePaths,
variableResolver
);
}
return detectAvailableUnixProfiles(
fsProvider,
logService,
includeDetectedProfiles,
profiles && isObject(profiles) ? { ...profiles } : configurationService.getValue<{ [key: string]: IUnresolvedTerminalProfile }>(isLinux ? TerminalSettingId.ProfilesLinux : TerminalSettingId.ProfilesMacOs),
isString(defaultProfile) ? defaultProfile : configurationService.getValue<string>(isLinux ? TerminalSettingId.DefaultProfileLinux : TerminalSettingId.DefaultProfileMacOs),
testPwshSourcePaths,
variableResolver,
shellEnv
);
}

We want a single consolidated place that the config is touched, otherwise features that depend on this scope in the future will need to reimplement, refactor, etc. It also introduces the chance of mismatches; what if a user and workspace profile have the same name for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added configScope property to ITerminalProfile and moved the config inspection logic to detectAvailableProfiles. The quickpick now uses this property to separate user and workspace profiles without re-inspecting configuration. (34e9571)

Copilot AI and others added 3 commits December 8, 2025 22:54
- Add configScope property to ITerminalProfile interface
- Detect and set configScope in detectAvailableProfiles function
- Update quickpick to use configScope instead of re-inspecting
- Separate user and workspace profiles based on configScope
- Addresses @Tyriar feedback about consolidated config inspection

Co-authored-by: Tyriar <2193314+Tyriar@users.noreply.github.com>
Co-authored-by: Tyriar <2193314+Tyriar@users.noreply.github.com>
Co-authored-by: Tyriar <2193314+Tyriar@users.noreply.github.com>
Copilot AI requested a review from Tyriar December 8, 2025 23:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Select Default Profile (terminal) doesn't work for projects with terminal specified

3 participants