需要一种更好的方法以编程方式清除和设置 vscode 中的用户设置

Need a better way to programmatically clear and set user settings in vscode

所以我正在为配置文件用户设置和已安装的扩展编写 vscode 配置文件管理扩展,但遇到了一些障碍。

对于我的扩展,我需要清除用户 settings.json 文件中当前设置的设置,但据我所知,无法通过扩展 API 获得settings.json 文件中键的精确表示,需要用 WorkspaceConfiguration.update("key", undefinded, ConfigurationTarget.Global) 函数删除设置。为此,我编写了这个函数:

/**
 * Clears out the Global (user) settings.json of all values.
 */
public async clearGlobalConfig() {
    // Due to limitations on updating configuration values, all possible
    // paths must be attempted for an update, from most specific to least
    const fullConfigPaths = mapObjectPaths(this.getGlobalConfig());
    // WorkspaceConfig object for doing config update operations
    const workspaceConfig = workspace.getConfiguration();

    // Iterate over all possible config paths
    for (const key of fullConfigPaths) {
        // Attempt to unset the value (will not unset till it matches the
        // exact path as it matches in the settings.json file)
        await workspaceConfig.update(key, undefined, ConfigurationTarget.Global);
    }
}

但是 运行 需要大约 10 - 20 秒,至少在我当前的设置下,并且每次删除设置时 window 都会闪烁。

下一个问题是从 settings.json 中获取设置以保存在配置文件配置中。当您执行 workspace.getConfguration().inspect("")?.globalValue 时,返回的对象不是带有 settings.json 的 1:1,所有带点符号的键都将被删除,对象将被展开。这很糟糕,因为现在,我再次不知道需要与 WorkspaceConfiguration.update() 一起使用的正确键来设置加载配置文件时的设置。

所以我编写了这个函数来尝试从 workspace.getConfiguration()... 调用中探测返回的对象,以获取 settings.json 文件的 1:1 表示以保存在配置文件配置中:

/**
 * Attempts to get an object that matches the contents of the user's
 * settings.json file exactly.
 * 
 * @returns An object that should match the contents of the user 
 * settings.json
 */
public async getGlobalSettingsFileContents(): Promise<Dictionary> {
    // Get the current user settings (minus this extension's settings)
    const runningConfig = this.getGlobalConfig();
    // Get all the possible full dot notated paths in the config object (from
    // most specific to least specific)
    const configPaths = mapObjectPaths(runningConfig);

    // Grab the WorkSpaceConfiguration object update function for reuse
    const updateFunction = workspace.getConfiguration().update;

    // The settings object with the proper settings.json keys to store in the
    // profile
    let actualSettings: Dictionary = {};

    // Previously checked config object path
    let previousPath = "";

    // Iterate over all the possible configuration object's dot paths
    for (const configPath of configPaths) {
        // If the previous path was a valid settings key AND the current path
        // is just a less specific object path
        if (has(actualSettings, previousPath) && startsWith(previousPath, configPath)) {
        continue;
        }

        // Get the current value before testing
        const currentValue = get(runningConfig, configPath);

        // Attempt to see if unsetting the settings value at this object path
        // works
        await updateFunction(configPath, undefined, ConfigurationTarget.Global);

        // Get the new value after the attempted unset
        const newValue = get(this.getGlobalConfig(), configPath);

        // If the value has changed (newValue probably being undefined)
        if (!isEqual(currentValue, newValue)) {
        // Then this was a valid settings key, save it
        actualSettings[configPath] = currentValue;
        }

        // Save path for checking against the next path
        previousPath = configPath;
    }


    // Get the actual settings keys that were just probed
    const actualConfigKeys = keys(actualSettings);

    // Iterate over the settings keys
    for (const configKey of actualConfigKeys) {
        // Attempt to re-add all the settings that were removed in the probing
        // process
        // WILL FAIL IF THERE ISN'T AN EXTENSION THAT CORRESPONDS TO THE SETTING
        await updateFunction(configKey, actualSettings[configKey], ConfigurationTarget.Global);
    }

    // Return what should be in the actual settings.json file
    return actualSettings;
}

但现在的问题是,在删除设置后,然后尝试在删除它们的确切密钥处重新添加它们,如果未安装相应的扩展程序,则会抛出错误:

所以现在我的问题是,是否有更好的方式以编程方式编辑用户 settings.json,最好是在 vscode API 内并且不需要直接执行文件系统操作?

Link 到项目:https://github.com/SLGShark6/vscode-profile-manager/tree/development

有点俗气,但我发现在 vscode 生态系统中编辑 settings.json 文件足够安全。

(注:Dictionary类型只是我自己定义的类型,大部分等同于object)

import * as JSONC from 'jsonc-parser';
import {
   commands,
   window,
   Range,
   Position
} from 'vscode';

/**
 * Gets the settings object directly from the settings.json file.
 * 
 * @returns Parsed object from the user settings.json file
 */
async function getSettingsJSON(): Promise<Dictionary> {
    // Open the user settings.json file in a new editor
    await commands.executeCommand("workbench.action.openSettingsJson");

    // Grab the new active editor from the window (should be the one we just
    // opened)
    const activeEditor = window.activeTextEditor!;
    // Get the open document from the active editor (should be the
    // settings.json file)
    const settingsDocument = activeEditor.document;

    // Get all of the text contained in the file
    const documentText = settingsDocument.getText();

    // Close the active settings.json editor
    await commands.executeCommand("workbench.action.closeActiveEditor");

    // Return the document parsed to a proper object
    return JSONC.parse(documentText);
}

/**
 * Writes a passed settings object directly to the user settings.json file,
 * overwriting the contents.
 * 
 * @param settings Settings object to overwrite the settings.json file with
 */
async function setSettingsJSON(settings: Dictionary) {
    // Open the user settings.json file in a new editor
    await commands.executeCommand("workbench.action.openSettingsJson");

    // Grab the new active editor from the window (should be the one we just
    // opened)
    const activeEditor = window.activeTextEditor!;
    // Get the open document from the active editor (should be the
    // settings.json file)
    const settingsDocument = activeEditor.document;

    // Open an edit operation to update the settings document
    activeEditor.edit((editBuilder) => {
        // Make a range from start to end of the document
        const startPosition = new Position(0,0);
        const endPosition = settingsDocument.lineAt(settingsDocument.lineCount - 1).rangeIncludingLineBreak.end;
        const range = new Range(startPosition, endPosition);

        // Replace everything with the new settings object
        editBuilder.replace(range, JSON.stringify(settings));
    });

    // Save the changed settings.json file
    await settingsDocument.save();
    // Close the active settings.json editor
    await commands.executeCommand("workbench.action.closeActiveEditor");
}