"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.apiVersion = exports.ICompletionsEditorAndPluginInfo = exports.BuildInfo = exports.InMemoryConfigProvider = exports.DefaultsOnlyConfigProvider = exports.ConfigProvider = exports.ICompletionsConfigProvider = exports.BuildType = exports.BlockMode = exports.ConfigKey = exports.packageJson = void 0;
exports.shouldDoServerTrimming = shouldDoServerTrimming;
exports.getConfigKeyRecursively = getConfigKeyRecursively;
exports.getConfigDefaultForKey = getConfigDefaultForKey;
exports.getOptionalConfigDefaultForKey = getOptionalConfigDefaultForKey;
exports.getConfig = getConfig;
exports.dumpForTelemetry = dumpForTelemetry;
exports.formatNameAndVersion = formatNameAndVersion;
exports.editorVersionHeaders = editorVersionHeaders;
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
const packagejson_1 = require("../../../../../platform/env/common/packagejson");
Object.defineProperty(exports, "packageJson", { enumerable: true, get: function () { return packagejson_1.packageJson; } });
const services_1 = require("../../../../../util/common/services");
const constants_1 = require("./constants");
const event_1 = require("./util/event");
exports.ConfigKey = {
    Enable: 'enable',
    UserSelectedCompletionModel: 'selectedCompletionModel',
    ShowEditorCompletions: 'editor.showEditorCompletions',
    EnableAutoCompletions: 'editor.enableAutoCompletions',
    DelayCompletions: 'editor.delayCompletions',
    FilterCompletions: 'editor.filterCompletions',
    CompletionsDelay: 'completionsDelay',
    CompletionsDebounce: 'completionsDebounce',
    // Advanced config (don't add new config here)
    RelatedFilesVSCodeCSharp: 'advanced.relatedFilesVSCodeCSharp',
    RelatedFilesVSCodeTypeScript: 'advanced.relatedFilesVSCodeTypeScript',
    RelatedFilesVSCode: 'advanced.relatedFilesVSCode',
    ContextProviders: 'advanced.contextProviders',
    DebugFilterLogCategories: 'advanced.debug.filterLogCategories',
    DebugSnippyOverrideUrl: 'advanced.debug.codeRefOverrideUrl',
    UseSubsetMatching: 'advanced.useSubsetMatching',
    ContextProviderTimeBudget: 'advanced.contextProviderTimeBudget',
    // Internal config
    DebugOverrideCapiUrl: 'internal.capiUrl',
    DebugOverrideCapiUrlLegacy: 'advanced.debug.overrideCapiUrl',
    DebugTestOverrideCapiUrl: 'internal.capiTestUrl',
    DebugTestOverrideCapiUrlLegacy: 'advanced.debug.testOverrideCapiUrl',
    DebugOverrideProxyUrl: 'internal.completionsUrl',
    DebugOverrideProxyUrlLegacy: 'advanced.debug.overrideProxyUrl',
    DebugTestOverrideProxyUrl: 'internal.completionsTestUrl',
    DebugTestOverrideProxyUrlLegacy: 'advanced.debug.testOverrideProxyUrl',
    DebugOverrideEngine: 'internal.completionModel',
    DebugOverrideEngineLegacy: 'advanced.debug.overrideEngine',
    /**
     * Internal experiment for always requesting multiline completions.
     * This might not result always in a multiline suggestion, but most often will.
     */
    AlwaysRequestMultiline: 'internal.alwaysRequestMultiline',
    /**
     * Let the model terminate single line completions when AlwaysRequestMultiline is enabled.
     */
    ModelAlwaysTerminatesSingleline: 'internal.modelAlwaysTerminatesSingleline',
    /**
     * Overrides whether to use the Workspace Context Coordinator to coordinate workspace context.
     * This setting takes precedence over the value from ExP.
     */
    UseWorkspaceContextCoordinator: 'internal.useWorkspaceContextCoordinator',
    /**
     * Overrides whether to include neighboring files in the prompt
     * alongside context providers.
     * This setting takes precedence over the value from ExP.
     */
    IncludeNeighboringFiles: 'internal.includeNeighboringFiles',
    ExcludeRelatedFiles: 'internal.excludeRelatedFiles',
    DebugOverrideCppHeadersEnableSwitch: 'internal.cppHeadersEnableSwitch',
    /**
     * Internal config for using the completions prompt with split context.
     * https://github.com/github/copilot/issues/19286
     */
    UseSplitContextPrompt: 'internal.useSplitContextPrompt',
};
// How to determine where to terminate the completion to the current block.
var BlockMode;
(function (BlockMode) {
    /**
     * Parse the context + completion on the client using treesitter to
     * determine blocks.
     */
    BlockMode["Parsing"] = "parsing";
    /**
     * Let the server parse out blocks and assume that the completion terminates
     * at the end of a block.
     */
    BlockMode["Server"] = "server";
    /**
     * Runs both the treesitter parsing on the client plus indentation-based
     * truncation on the proxy.
     */
    BlockMode["ParsingAndServer"] = "parsingandserver";
    /**
     * Client-based heuristic to display more multiline completions.
     * It almost always requests a multiline completion from the server and tries to break it up to something useful on the client.
     *
     * This should not be rolled out at the moment (latency impact is high, UX needs further fine-tuning),
     * but can  be used for internal experimentation.
     */
    BlockMode["MoreMultiline"] = "moremultiline";
})(BlockMode || (exports.BlockMode = BlockMode = {}));
function shouldDoServerTrimming(blockMode) {
    return [BlockMode.Server, BlockMode.ParsingAndServer].includes(blockMode);
}
// TODO rework this enum so that the normal/nightly and prod/dev distinctions are orthogonal. (dev builds should behave like nightly?)
var BuildType;
(function (BuildType) {
    BuildType["DEV"] = "dev";
    BuildType["PROD"] = "prod";
    BuildType["NIGHTLY"] = "nightly";
})(BuildType || (exports.BuildType = BuildType = {}));
exports.ICompletionsConfigProvider = (0, services_1.createServiceIdentifier)('ICompletionsConfigProvider');
class ConfigProvider {
    // The language server receives workspace configuration *after* it is fully initialized, which creates a race
    // condition where an incoming request immediately after initialization might have the default values. Awaiting
    // this promise allows consumers to ensure that the configuration is ready before using it.
    requireReady() {
        return Promise.resolve();
    }
}
exports.ConfigProvider = ConfigProvider;
/** Provides only the default values, ignoring the user's settings.
 * @public KEEPING FOR TESTS
*/
class DefaultsOnlyConfigProvider extends ConfigProvider {
    constructor() {
        super(...arguments);
        this.onDidChangeCopilotSettings = () => {
            // no-op, since this provider does not support changing settings
            return {
                dispose: () => { },
            };
        };
    }
    getConfig(key) {
        // hardcode default values for the agent, for now
        return getConfigDefaultForKey(key);
    }
    getOptionalConfig(key) {
        return getOptionalConfigDefaultForKey(key);
    }
    dumpForTelemetry() {
        return {};
    }
}
exports.DefaultsOnlyConfigProvider = DefaultsOnlyConfigProvider;
/**
 * A ConfigProvider that allows overriding of config values.
 * @public KEEPING FOR TESTS
*/
class InMemoryConfigProvider extends ConfigProvider {
    constructor(baseConfigProvider) {
        super();
        this.baseConfigProvider = baseConfigProvider;
        this.copilotEmitter = new event_1.Emitter();
        this.onDidChangeCopilotSettings = this.copilotEmitter.event;
        this.overrides = new Map();
    }
    setOverrides(overrides) {
        this.overrides = overrides;
    }
    clearOverrides() {
        this.overrides.clear();
    }
    getOptionalOverride(key) {
        return this.overrides.get(key);
    }
    getConfig(key) {
        return this.getOptionalOverride(key) ?? this.baseConfigProvider.getConfig(key);
    }
    getOptionalConfig(key) {
        return this.getOptionalOverride(key) ?? this.baseConfigProvider.getOptionalConfig(key);
    }
    setConfig(key, value) {
        this.setCopilotSettings({ [key]: value });
    }
    setCopilotSettings(settings) {
        for (const [key, value] of Object.entries(settings)) {
            if (value !== undefined) {
                this.overrides.set(key, value);
            }
            else {
                this.overrides.delete(key);
            }
        }
        this.copilotEmitter.fire(this);
    }
    dumpForTelemetry() {
        const config = this.baseConfigProvider.dumpForTelemetry();
        // reflects what's mapped in Hydro
        for (const key of [
            exports.ConfigKey.ShowEditorCompletions,
            exports.ConfigKey.EnableAutoCompletions,
            exports.ConfigKey.DelayCompletions,
            exports.ConfigKey.FilterCompletions,
        ]) {
            const value = this.overrides.get(key);
            if (value !== undefined) {
                config[key] = JSON.stringify(value);
            }
        }
        return config;
    }
}
exports.InMemoryConfigProvider = InMemoryConfigProvider;
function getConfigKeyRecursively(config, key) {
    let value = config;
    const prefix = [];
    for (const segment of key.split('.')) {
        const child = [...prefix, segment].join('.');
        if (value && typeof value === 'object' && child in value) {
            value = value[child];
            prefix.length = 0;
        }
        else {
            prefix.push(segment);
        }
    }
    if (value === undefined || prefix.length > 0) {
        return;
    }
    return value;
}
function getConfigDefaultForKey(key) {
    if (configDefaults.has(key)) {
        return configDefaults.get(key);
    }
    throw new Error(`Missing config default value: ${constants_1.CopilotConfigPrefix}.${key}`);
}
function getOptionalConfigDefaultForKey(key) {
    return configDefaults.get(key);
}
/**
 * Defaults for "hidden" config keys.  These are supplemented by the defaults in package.json.
 */
const configDefaults = new Map([
    [exports.ConfigKey.DebugOverrideCppHeadersEnableSwitch, false],
    [exports.ConfigKey.RelatedFilesVSCodeCSharp, false],
    [exports.ConfigKey.RelatedFilesVSCodeTypeScript, false],
    [exports.ConfigKey.RelatedFilesVSCode, false],
    [exports.ConfigKey.IncludeNeighboringFiles, false],
    [exports.ConfigKey.ExcludeRelatedFiles, false],
    [exports.ConfigKey.ContextProviders, []],
    [exports.ConfigKey.DebugSnippyOverrideUrl, ''],
    [exports.ConfigKey.UseSubsetMatching, null],
    [exports.ConfigKey.ContextProviderTimeBudget, undefined],
    [exports.ConfigKey.DebugOverrideCapiUrl, ''],
    [exports.ConfigKey.DebugTestOverrideCapiUrl, ''],
    [exports.ConfigKey.DebugOverrideProxyUrl, ''],
    [exports.ConfigKey.DebugTestOverrideProxyUrl, ''],
    [exports.ConfigKey.DebugOverrideEngine, ''],
    [exports.ConfigKey.AlwaysRequestMultiline, undefined],
    [exports.ConfigKey.CompletionsDebounce, undefined],
    [exports.ConfigKey.CompletionsDelay, undefined],
    [exports.ConfigKey.ModelAlwaysTerminatesSingleline, undefined],
    [exports.ConfigKey.UseWorkspaceContextCoordinator, undefined],
    // These are only used for telemetry from LSP based editors and do not affect any behavior.
    [exports.ConfigKey.ShowEditorCompletions, undefined],
    [exports.ConfigKey.EnableAutoCompletions, undefined],
    [exports.ConfigKey.DelayCompletions, undefined],
    [exports.ConfigKey.FilterCompletions, undefined],
    [exports.ConfigKey.UseSplitContextPrompt, true],
    // These are defaults from package.json
    [exports.ConfigKey.Enable, { '*': true, 'plaintext': false, 'markdown': false, 'scminput': false }],
    [exports.ConfigKey.UserSelectedCompletionModel, ''],
    // These are advanced defaults from package.json
    [exports.ConfigKey.DebugOverrideEngineLegacy, ''],
    [exports.ConfigKey.DebugOverrideProxyUrlLegacy, ''],
    [exports.ConfigKey.DebugTestOverrideProxyUrlLegacy, ''],
    [exports.ConfigKey.DebugOverrideCapiUrlLegacy, ''],
    [exports.ConfigKey.DebugTestOverrideCapiUrlLegacy, ''],
    [exports.ConfigKey.DebugFilterLogCategories, []],
]);
function getConfig(accessor, key) {
    return accessor.get(exports.ICompletionsConfigProvider).getConfig(key);
}
function dumpForTelemetry(accessor) {
    try {
        return accessor.get(exports.ICompletionsConfigProvider).dumpForTelemetry();
    }
    catch (e) {
        console.error(`Error dumping config for telemetry: ${e}`);
        return {};
    }
}
class BuildInfo {
    static isPreRelease() {
        return this.getBuildType() === BuildType.NIGHTLY;
    }
    static isProduction() {
        return this.getBuildType() !== BuildType.DEV;
    }
    static getBuildType() {
        const buildType = packagejson_1.packageJson.buildType;
        if (buildType === 'prod') {
            return BuildInfo.getVersion().length === 15 ? BuildType.NIGHTLY : BuildType.PROD;
        }
        return BuildType.DEV;
    }
    static getVersion() {
        return packagejson_1.packageJson.version;
    }
    static getBuild() {
        return packagejson_1.packageJson.build;
    }
}
exports.BuildInfo = BuildInfo;
function formatNameAndVersion({ name, version }) {
    return `${name}/${version}`;
}
exports.ICompletionsEditorAndPluginInfo = (0, services_1.createServiceIdentifier)('ICompletionsEditorAndPluginInfo');
/**
 * Do not use this in new code.  Every endpoint has its own unique versioning.
 * Centralizing in a single constant was a mistake.
 * @deprecated
 */
exports.apiVersion = '2025-05-01';
function editorVersionHeaders(accessor) {
    const info = accessor.get(exports.ICompletionsEditorAndPluginInfo);
    return {
        'Editor-Version': formatNameAndVersion(info.getEditorInfo()),
        'Editor-Plugin-Version': formatNameAndVersion(info.getEditorPluginInfo()),
        'Copilot-Language-Server-Version': BuildInfo.getVersion(),
    };
}
//# sourceMappingURL=config.js.map