"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.StatelessNextEditTelemetryBuilder = exports.StatelessNextEditResult = exports.NoNextEditReason = exports.FilteredOutReason = exports.StatelessNextEditDocument = exports.StatelessNextEditRequest = exports.RequestEditWindowWithCursorJump = exports.RequestEditWindow = exports.WithStatelessProviderTelemetry = void 0;
const result_1 = require("../../../util/common/result");
const assert_1 = require("../../../util/vs/base/common/assert");
const async_1 = require("../../../util/vs/base/common/async");
const cancellation_1 = require("../../../util/vs/base/common/cancellation");
const stringEdit_1 = require("../../../util/vs/editor/common/core/edits/stringEdit");
const abstractText_1 = require("../../../util/vs/editor/common/core/text/abstractText");
const workspaceLog_1 = require("../../workspaceRecorder/common/workspaceLog");
const stringifyChatMessages_1 = require("./utils/stringifyChatMessages");
class WithStatelessProviderTelemetry {
    constructor(v, telemetryBuilder) {
        this.v = v;
        this.telemetryBuilder = telemetryBuilder;
    }
}
exports.WithStatelessProviderTelemetry = WithStatelessProviderTelemetry;
class RequestEditWindow {
    constructor(window) {
        this.window = window;
    }
    containsCursor(cursor) {
        return this.window.containsRange(cursor);
    }
}
exports.RequestEditWindow = RequestEditWindow;
class RequestEditWindowWithCursorJump {
    constructor(window, originalWindow) {
        this.window = window;
        this.originalWindow = originalWindow;
    }
    containsCursor(cursor) {
        return this.window.containsRange(cursor) || this.originalWindow.containsRange(cursor);
    }
}
exports.RequestEditWindowWithCursorJump = RequestEditWindowWithCursorJump;
class StatelessNextEditRequest {
    static { this.ID = 0; }
    get result() {
        return this._result.p;
    }
    constructor(headerRequestId, opportunityId, 
    /** this's the active document current contents (not sure "before" which edits this's named after -- maybe NES edits) */
    documentBeforeEdits, documents, activeDocumentIdx, xtabEditHistory, firstEdit, expandedEditWindowNLines, isSpeculative, logContext, recordingBookmark, recording, providerRequestStartDateTime) {
        this.headerRequestId = headerRequestId;
        this.opportunityId = opportunityId;
        this.documentBeforeEdits = documentBeforeEdits;
        this.documents = documents;
        this.activeDocumentIdx = activeDocumentIdx;
        this.xtabEditHistory = xtabEditHistory;
        this.firstEdit = firstEdit;
        this.expandedEditWindowNLines = expandedEditWindowNLines;
        this.isSpeculative = isSpeculative;
        this.logContext = logContext;
        this.recordingBookmark = recordingBookmark;
        this.recording = recording;
        this.providerRequestStartDateTime = providerRequestStartDateTime;
        this.seqid = String(++StatelessNextEditRequest.ID);
        this.cancellationTokenSource = new cancellation_1.CancellationTokenSource();
        this.liveDependentants = 0; // number of invocations which haven't been canceled and depend on this request
        this.fetchIssued = false;
        this.intermediateUserEdit = stringEdit_1.StringEdit.empty;
        this._result = new async_1.DeferredPromise();
        (0, assert_1.assert)(documents.length > 0);
        (0, assert_1.assert)(activeDocumentIdx >= 0 && activeDocumentIdx < documents.length);
    }
    setResult(nextEditResult) {
        this._result.complete(nextEditResult);
    }
    setResultError(err) {
        this._result.error(err);
    }
    hasDocument(docId) {
        return this.documents.find(d => d.id === docId) !== undefined;
    }
    getActiveDocument() {
        return this.documents[this.activeDocumentIdx];
    }
    serialize() {
        return {
            id: this.headerRequestId,
            documents: this.documents.map(d => d.serialize()),
            activeDocumentIdx: this.activeDocumentIdx,
            recording: this.recording,
        };
    }
    toString() {
        return this.toMarkdown();
    }
    toMarkdown() {
        const docs = this.documents.map((d, idx) => ` * [${idx + 1}/${this.documents.length}] ${idx === this.activeDocumentIdx ? '(active document) ' : ''}` + d.toMarkdown()).join('\n\n');
        return `### StatelessNextEditRequest\n\n${docs}`;
    }
}
exports.StatelessNextEditRequest = StatelessNextEditRequest;
class StatelessNextEditDocument {
    /**
     * NOTE: if you add new public fields to this class, please also update {@link ISerializedNextEditDocument} and {@link serialize()} methods,
     * which are used to send this to http-server-powered NES provider.
     */
    constructor(id, workspaceRoot, languageId, documentLinesBeforeEdit, recentEdit, documentBeforeEdits, recentEdits, lastSelectionInAfterEdit = undefined) {
        this.id = id;
        this.workspaceRoot = workspaceRoot;
        this.languageId = languageId;
        this.documentLinesBeforeEdit = documentLinesBeforeEdit;
        this.recentEdit = recentEdit;
        this.documentBeforeEdits = documentBeforeEdits;
        this.recentEdits = recentEdits;
        this.lastSelectionInAfterEdit = lastSelectionInAfterEdit;
        this.documentAfterEdits = new abstractText_1.StringText(this.recentEdits.apply(this.documentBeforeEdits.value));
        this.documentAfterEditsLines = this.documentAfterEdits.getLines();
    }
    serialize() {
        return {
            id: this.id.uri,
            workspaceRoot: this.workspaceRoot?.toString(),
            languageId: this.languageId,
            documentLinesBeforeEdit: this.documentLinesBeforeEdit,
            recentEdit: this.recentEdit.serialize(),
            documentBeforeEdits: this.documentBeforeEdits.value,
            recentEdits: this.recentEdits.serialize(),
            lastSelectionInAfterEdit: this.lastSelectionInAfterEdit === undefined ? undefined : (0, workspaceLog_1.serializeOffsetRange)(this.lastSelectionInAfterEdit),
        };
    }
    toString() {
        return this.toMarkdown();
    }
    toMarkdown() {
        const lines = [];
        lines.push(`StatelessNextEditDocument: **${this.id.uri}**\n`);
        lines.push('```patch');
        lines.push(this.recentEdit.humanReadablePatch(this.documentLinesBeforeEdit));
        lines.push('```');
        lines.push('');
        return lines.join('\n');
    }
}
exports.StatelessNextEditDocument = StatelessNextEditDocument;
var FilteredOutReason;
(function (FilteredOutReason) {
    FilteredOutReason["LowLogProbSuggestions"] = "lowLogProbSuggestions";
    FilteredOutReason["EnforcingNextEditOptions"] = "enforcingNextEditOptions";
    FilteredOutReason["PromptTooLarge"] = "promptTooLarge";
    FilteredOutReason["Uncategorized"] = "uncategorized";
})(FilteredOutReason || (exports.FilteredOutReason = FilteredOutReason = {}));
var NoNextEditReason;
(function (NoNextEditReason_1) {
    class NoNextEditReason {
    }
    class ActiveDocumentHasNoEdits extends NoNextEditReason {
        constructor() {
            super(...arguments);
            this.kind = 'activeDocumentHasNoEdits';
        }
        toString() {
            return this.kind;
        }
    }
    NoNextEditReason_1.ActiveDocumentHasNoEdits = ActiveDocumentHasNoEdits;
    class NoSuggestions extends NoNextEditReason {
        constructor(documentBeforeEdits, window, nextCursorPosition, nextCursorDocumentId) {
            super();
            this.documentBeforeEdits = documentBeforeEdits;
            this.window = window;
            this.nextCursorPosition = nextCursorPosition;
            this.nextCursorDocumentId = nextCursorDocumentId;
            this.kind = 'noSuggestions';
        }
        toString() {
            return this.kind;
        }
    }
    NoNextEditReason_1.NoSuggestions = NoSuggestions;
    class GotCancelled extends NoNextEditReason {
        constructor(message) {
            super();
            this.message = message;
            this.kind = 'gotCancelled';
        }
        toString() {
            return `${this.kind}:${this.message}`;
        }
    }
    NoNextEditReason_1.GotCancelled = GotCancelled;
    class FetchFailure extends NoNextEditReason {
        constructor(error) {
            super();
            this.error = error;
            this.kind = 'fetchFailure';
        }
        toString() {
            return `${this.kind}:${this.error.message}`;
        }
    }
    NoNextEditReason_1.FetchFailure = FetchFailure;
    class FilteredOut extends NoNextEditReason {
        constructor(message) {
            super();
            this.message = message;
            this.kind = 'filteredOut';
        }
        toString() {
            return `${this.kind}:${this.message}`;
        }
    }
    NoNextEditReason_1.FilteredOut = FilteredOut;
    class PromptTooLarge extends NoNextEditReason {
        constructor(message) {
            super();
            this.message = message;
            this.kind = 'promptTooLarge';
        }
        toString() {
            return `${this.kind}:${this.message}`;
        }
    }
    NoNextEditReason_1.PromptTooLarge = PromptTooLarge;
    class Uncategorized extends NoNextEditReason {
        constructor(error) {
            super();
            this.error = error;
            this.kind = 'uncategorized';
        }
        toString() {
            return `${this.kind}:${this.error.message}`;
        }
    }
    NoNextEditReason_1.Uncategorized = Uncategorized;
    class Unexpected extends NoNextEditReason {
        constructor(error) {
            super();
            this.error = error;
            this.kind = 'unexpected';
        }
        toString() {
            return `${this.kind}:${this.error.message}`;
        }
    }
    NoNextEditReason_1.Unexpected = Unexpected;
})(NoNextEditReason || (exports.NoNextEditReason = NoNextEditReason = {}));
class StatelessNextEditResult {
    static noEdit(reason, telemetryBuilder) {
        const result = result_1.Result.error(reason);
        const telemetry = telemetryBuilder.build(result);
        return new StatelessNextEditResult(result, telemetry);
    }
    static streaming(telemetryBuilder) {
        const result = result_1.Result.ok(undefined);
        const telemetry = telemetryBuilder.build(result);
        return new StatelessNextEditResult(result, telemetry);
    }
    constructor(nextEdit, telemetry) {
        this.nextEdit = nextEdit;
        this.telemetry = telemetry;
    }
}
exports.StatelessNextEditResult = StatelessNextEditResult;
class StatelessNextEditTelemetryBuilder {
    /**
     * It takes a request to automatically capture some properties from the request.
     */
    constructor(headerRequestId) {
        this._nextCursorPrediction = {
            nextCursorLineError: undefined,
            nextCursorLineDistance: undefined,
            isCrossFile: undefined
        };
        this.startTime = Date.now();
        this.requestUuid = headerRequestId;
    }
    build(result) {
        const endTime = Date.now();
        const timeSpent = endTime - this.startTime;
        const prompt = this._prompt ? JSON.stringify(this._prompt.map(({ role, content }) => ({ role, content }))) : undefined;
        const promptText = this._prompt ? (0, stringifyChatMessages_1.stringifyChatMessages)(this._prompt) : undefined;
        const promptLineCount = promptText?.split('\n').length;
        const promptCharCount = promptText?.length;
        const noNextEditReasonKind = result.isOk() ? undefined : result.err.kind;
        let noNextEditReasonMessage;
        if (result.isError()) {
            if (result.err instanceof NoNextEditReason.ActiveDocumentHasNoEdits || result.err instanceof NoNextEditReason.NoSuggestions) {
                // ignore
            }
            else if (result.err instanceof NoNextEditReason.GotCancelled || result.err instanceof NoNextEditReason.FilteredOut || result.err instanceof NoNextEditReason.PromptTooLarge) {
                noNextEditReasonMessage = result.err.message;
            }
            else if (result.err instanceof NoNextEditReason.FetchFailure || result.err instanceof NoNextEditReason.Uncategorized || result.err instanceof NoNextEditReason.Unexpected) {
                noNextEditReasonMessage = result.err.error.stack ? result.err.error.stack : result.err.error.message;
            }
            else {
                (0, assert_1.assertNever)(result.err);
            }
        }
        return {
            hadStatelessNextEditProviderCall: true,
            noNextEditReasonKind,
            noNextEditReasonMessage,
            statelessNextEditProviderDuration: timeSpent,
            logProbThreshold: this._logProbThreshold,
            mergeConflictExpanded: this._mergeConflictExpanded,
            nLinesOfCurrentFileInPrompt: this._nLinesOfCurrentFileInPrompt,
            modelName: this._modelName,
            prompt,
            promptLineCount,
            promptCharCount,
            isCursorAtEndOfLine: this._isCursorAtLineEnd,
            isInlineSuggestion: this._isInlineSuggestion,
            debounceTime: this._debounceTime,
            artificialDelay: this._artificialDelay,
            fetchStartedAt: this._fetchStartedAt,
            hadLowLogProbSuggestion: this._hadLowLogProbSuggestion,
            response: this._response,
            nEditsSuggested: this._nEditsSuggested,
            nextEditLogprob: this._nextEditLogProb,
            nextCursorPrediction: this._nextCursorPrediction,
            lineDistanceToMostRecentEdit: this._lineDistanceToMostRecentEdit,
            xtabAggressivenessLevel: this._xtabAggressivenessLevel,
            xtabUserHappinessScore: this._xtabUserHappinessScore,
            userAggressivenessSetting: this._userAggressivenessSetting,
            editIntent: this._editIntent,
            editIntentParseError: this._editIntentParseError,
            cursorJumpModelName: this._cursorJumpModelName,
            cursorJumpPrompt: this._cursorJumpPrompt ? JSON.stringify(this._cursorJumpPrompt.map(({ role, content }) => ({ role, content }))) : undefined,
            cursorJumpResponse: this._cursorJumpResponse,
            nDiffsInPrompt: this._nDiffsInPrompt,
            diffTokensInPrompt: this._diffTokensInPrompt,
            nNeighborSnippetsComputed: this._nNeighborSnippetsComputed,
            nNeighborSnippetsInPrompt: this._nNeighborSnippetsInPrompt,
            neighborSnippetIndicesInPrompt: this._neighborSnippetIndicesInPrompt,
            lintErrors: this._lintErrors,
            terminalOutput: this._terminalOutput,
            similarFilesContext: this._similarFilesContext,
            modelConfig: this._modelConfig,
        };
    }
    setLogProbThreshold(logProbThreshold) {
        this._logProbThreshold = logProbThreshold;
        return this;
    }
    setMergeConflictExpanded(mergeConflictExpanded) {
        this._mergeConflictExpanded = mergeConflictExpanded;
        return this;
    }
    setHadLowLogProbSuggestion(hadLowLogProbSuggestions) {
        this._hadLowLogProbSuggestion = hadLowLogProbSuggestions;
        return this;
    }
    setNLinesOfCurrentFileInPrompt(nLines) {
        this._nLinesOfCurrentFileInPrompt = nLines;
        return this;
    }
    setModelName(modelName) {
        this._modelName = modelName;
        return this;
    }
    setPrompt(prompt) {
        this._prompt = prompt;
        return this;
    }
    setIsCursorAtLineEnd(isCursorAtLineEnd) {
        this._isCursorAtLineEnd = isCursorAtLineEnd;
        return this;
    }
    setIsInlineSuggestion(isInlineSuggestion) {
        this._isInlineSuggestion = isInlineSuggestion;
        return this;
    }
    setDebounceTime(debounceTime) {
        this._debounceTime = debounceTime;
        return this;
    }
    setArtificialDelay(artificialDelay) {
        this._artificialDelay = artificialDelay;
        return this;
    }
    setFetchStartedAt() {
        this._fetchStartedAt = Date.now();
        return this;
    }
    get fetchStartedAt() {
        return this._fetchStartedAt;
    }
    setResponse(response) {
        this._response = response.then(({ response, ttft }) => {
            const fetchTime = Date.now() - this._fetchStartedAt;
            const fetchResult = response.type;
            return {
                ttft,
                response,
                fetchTime,
                fetchResult,
            };
        });
        return this;
    }
    setCursorJumpModelName(modelName) {
        this._cursorJumpModelName = modelName;
        return this;
    }
    setCursorJumpPrompt(prompt) {
        this._cursorJumpPrompt = prompt;
        return this;
    }
    setCursorJumpResponse(response) {
        this._cursorJumpResponse = response;
        return this;
    }
    setNextEditLogProb(logProb) {
        this._nextEditLogProb = logProb;
        return this;
    }
    setNEditsSuggested(nEditsSuggested) {
        this._nEditsSuggested = nEditsSuggested;
        return this;
    }
    setLineDistanceToMostRecentEdit(distanceToMostRecentEdit) {
        this._lineDistanceToMostRecentEdit = distanceToMostRecentEdit;
        return this;
    }
    setNextCursorLineError(error) {
        this._nextCursorPrediction.nextCursorLineError = error;
        return this;
    }
    /**
     * nextCursorLineNumber - currentCursorLineNumber
     */
    setNextCursorLineDistance(distance) {
        this._nextCursorPrediction.nextCursorLineDistance = distance;
        return this;
    }
    setNextCursorIsCrossFile(isCrossFile) {
        this._nextCursorPrediction.isCrossFile = isCrossFile;
        return this;
    }
    setXtabAggressivenessLevel(level) {
        this._xtabAggressivenessLevel = level;
        return this;
    }
    setXtabUserHappinessScore(score) {
        this._xtabUserHappinessScore = score;
        return this;
    }
    setUserAggressivenessSetting(setting) {
        this._userAggressivenessSetting = setting;
        return this;
    }
    setEditIntent(editIntent) {
        this._editIntent = editIntent;
        return this;
    }
    setEditIntentParseError(error) {
        this._editIntentParseError = error;
        return this;
    }
    setNDiffsInPrompt(n) {
        this._nDiffsInPrompt = n;
        return this;
    }
    setDiffTokensInPrompt(n) {
        this._diffTokensInPrompt = n;
        return this;
    }
    setNNeighborSnippetsComputed(n) {
        this._nNeighborSnippetsComputed = n;
        return this;
    }
    setNNeighborSnippetsInPrompt(n) {
        this._nNeighborSnippetsInPrompt = n;
        return this;
    }
    setNeighborSnippetIndicesInPrompt(indices) {
        this._neighborSnippetIndicesInPrompt = JSON.stringify(indices);
        return this;
    }
    setLintErrors(lintErrors) {
        this._lintErrors = lintErrors;
        return this;
    }
    setTerminalOutput(terminalOutput) {
        this._terminalOutput = terminalOutput;
        return this;
    }
    setSimilarFilesContext(similarFilesContext) {
        this._similarFilesContext = similarFilesContext;
        return this;
    }
    setModelConfig(modelConfig) {
        this._modelConfig = modelConfig;
        return this;
    }
}
exports.StatelessNextEditTelemetryBuilder = StatelessNextEditTelemetryBuilder;
//# sourceMappingURL=statelessNextEditProvider.js.map