|
download
raw
8.7 kB
metadata
name: settings-precedence
description: >-
  VS Code settings precedence rules and common pitfalls. Essential for any code
  that reads or writes settings. Covers getConfiguration scope, inspect() vs
  get(), and multi-workspace handling.
argument-hint: Review settings handling in [file or component]
user-invocable: false

VS Code Settings Precedence

Settings precedence bugs corrupt user configurations. This skill documents the correct patterns.

Precedence Order (Highest to Lowest)

  1. Workspace folder value - Per-folder in multi-root workspace
  2. Workspace value - .vscode/settings.json or .code-workspace
  3. User/global value - User settings.json
  4. Default value - From extension's package.json (⚠️ may come from other extensions!)

Core Rules

Rule 1: Always Pass Scope to getConfiguration()

// ❌ WRONG: Missing scope
const config = vscode.workspace.getConfiguration('python-envs');
const value = config.get('pythonProjects');
// workspaceFolderValue will be UNDEFINED because VS Code doesn't know which folder!

// ✅ RIGHT: Pass scope (workspace folder or document URI)
const config = vscode.workspace.getConfiguration('python-envs', workspaceFolder);
const value = config.get('pythonProjects');

When to pass scope:

  • Reading per-resource settings (scope: "resource" in package.json)
  • Any multi-workspace scenario
  • When you need workspaceFolderValue from inspect()

Rule 2: Use inspect() to Check Explicit Values

// ❌ WRONG: get() returns defaultValue even from other extensions!
const config = vscode.workspace.getConfiguration('python');
if (config.get('useEnvironmentsExtension')) {
    // May return true from another extension's package.json default!
}

// ✅ RIGHT: Use inspect() and check explicit values only
const config = vscode.workspace.getConfiguration('python', scope);
const inspected = config.inspect('useEnvironmentsExtension');

const hasExplicitValue =
    inspected?.globalValue !== undefined ||
    inspected?.workspaceValue !== undefined ||
    inspected?.workspaceFolderValue !== undefined;

if (hasExplicitValue) {
    // User explicitly set this value
    const effectiveValue = inspected?.workspaceFolderValue ?? inspected?.workspaceValue ?? inspected?.globalValue;
}

Rule 3: Don't Overwrite User's Explicit Values

// ❌ WRONG: Unconditionally writing to settings
await config.update('pythonPath', detectedPath, ConfigurationTarget.Workspace);
// Overwrites user's explicit choice!

// ✅ RIGHT: Check for existing explicit values first
const inspected = config.inspect('pythonPath');
const hasUserValue = inspected?.workspaceValue !== undefined;

if (!hasUserValue) {
    // Only set if user hasn't explicitly chosen
    await config.update('pythonPath', detectedPath, ConfigurationTarget.Workspace);
}

Rule 4: Update at the Correct Scope

// Configuration targets (least to most specific)
ConfigurationTarget.Global; // User settings.json
ConfigurationTarget.Workspace; // .vscode/settings.json or .code-workspace
ConfigurationTarget.WorkspaceFolder; // Per-folder in multi-root

// To remove a setting, update with undefined
await config.update('pythonPath', undefined, ConfigurationTarget.Workspace);

Multi-Root Workspace Handling

The workspace Property

For multi-root workspaces, pythonProjects settings need a workspace property:

{
    "python-envs.pythonProjects": [
        {
            "path": ".",
            "workspace": "/path/to/workspace-folder",
            "envManager": "ms-python.python:venv"
        }
    ]
}

Without the workspace property, settings get mixed up between folders.

Getting the Right Workspace Folder

// ❌ WRONG: Always using first workspace folder
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];

// ✅ RIGHT: Get folder for specific document/file
const workspaceFolder = vscode.workspace.getWorkspaceFolder(documentUri) ?? vscode.workspace.workspaceFolders?.[0];

// When you have a path but not a URI
const uri = vscode.Uri.file(filePath);
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);

Common Issues

Issue: workspaceFolderValue is undefined

Cause: Missing scope parameter in getConfiguration()

// This returns undefined for workspaceFolderValue!
const config = vscode.workspace.getConfiguration('python-envs');
const inspected = config.inspect('pythonProjects');
console.log(inspected?.workspaceFolderValue); // undefined!

// Fix: Pass scope
const config = vscode.workspace.getConfiguration('python-envs', workspaceFolder.uri);
const inspected = config.inspect('pythonProjects');
console.log(inspected?.workspaceFolderValue); // Now works!

Issue: defaultValue from other extensions

Cause: Using get() instead of inspect() for boolean checks

The defaultValue in inspect() may come from ANY extension's package.json, not just yours:

// Another extension might have in their package.json:
// "python.useEnvironmentsExtension": { "default": true }

// Your check will be wrong:
config.get('python.useEnvironmentsExtension') // true from other extension!

// Fix: Only check explicit values
const inspected = config.inspect('python.useEnvironmentsExtension');
if (inspected?.globalValue === true || ...) { }

Issue: Settings overwritten on reload

Cause: Not checking for existing values before writing

// During extension activation, this overwrites user's config!
await config.update('defaultEnvManager', 'venv', ConfigurationTarget.Global);

// Fix: Only write defaults if no value exists
const current = config.inspect('defaultEnvManager');
if (current?.globalValue === undefined && current?.workspaceValue === undefined) {
    await config.update('defaultEnvManager', 'venv', ConfigurationTarget.Global);
}

Issue: Settings mixed up in multi-root

Cause: Not including workspace identifier in settings

// Without workspace identifier, can't tell which folder this belongs to
{
    "python-envs.pythonProjects": [
        { "path": ".", "envManager": "venv" }  // Which workspace?
    ]
}

// Fix: Always include workspace when saving
const project = {
    path: projectPath,
    workspace: workspaceFolder.uri.fsPath,
    envManager: selectedManager
};

Complete Example: Safe Settings Read/Write

import * as vscode from 'vscode';

async function getProjectConfig(projectUri: vscode.Uri): Promise<ProjectConfig | undefined> {
    const workspaceFolder = vscode.workspace.getWorkspaceFolder(projectUri);
    if (!workspaceFolder) {
        return undefined;
    }

    // Always pass scope!
    const config = vscode.workspace.getConfiguration('python-envs', workspaceFolder.uri);

    // Use inspect() to understand where values come from
    const inspected = config.inspect<ProjectConfig[]>('pythonProjects');

    // Prefer most specific value
    const projects = inspected?.workspaceFolderValue ?? inspected?.workspaceValue ?? inspected?.globalValue ?? []; // Don't use defaultValue!

    // Find project matching the URI
    return projects.find((p) => path.resolve(workspaceFolder.uri.fsPath, p.path) === projectUri.fsPath);
}

async function saveProjectConfig(projectUri: vscode.Uri, projectConfig: ProjectConfig): Promise<void> {
    const workspaceFolder = vscode.workspace.getWorkspaceFolder(projectUri);
    if (!workspaceFolder) {
        return;
    }

    const config = vscode.workspace.getConfiguration('python-envs', workspaceFolder.uri);

    const inspected = config.inspect<ProjectConfig[]>('pythonProjects');

    // Get existing projects (not including defaults!)
    const existingProjects = inspected?.workspaceFolderValue ?? inspected?.workspaceValue ?? [];

    // Ensure workspace property for multi-root
    const configToSave: ProjectConfig = {
        ...projectConfig,
        workspace: workspaceFolder.uri.fsPath,
    };

    // Update or add
    const projectIndex = existingProjects.findIndex(
        (p) => path.resolve(workspaceFolder.uri.fsPath, p.path) === projectUri.fsPath,
    );

    const updatedProjects = [...existingProjects];
    if (projectIndex >= 0) {
        updatedProjects[projectIndex] = configToSave;
    } else {
        updatedProjects.push(configToSave);
    }

    // Write to workspace folder scope in multi-root
    const target =
        vscode.workspace.workspaceFolders?.length > 1
            ? vscode.ConfigurationTarget.WorkspaceFolder
            : vscode.ConfigurationTarget.Workspace;

    await config.update('pythonProjects', updatedProjects, target);
}

Xet Storage Details

Size:
8.7 kB
·
Xet hash:
b7972bb61923eba19342200b775dd2bc9dbf0a80fe2e9eb5ca4c508dd0b43e28

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.