mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 08:15:56 +01:00
inlineChat: shared history service with persistence (#303471)
This commit is contained in:
@@ -17,6 +17,7 @@ import { InlineChatNotebookContribution } from './inlineChatNotebook.js';
|
||||
import { IWorkbenchContributionsRegistry, registerWorkbenchContribution2, Extensions as WorkbenchExtensions, WorkbenchPhase } from '../../../common/contributions.js';
|
||||
import { IInlineChatSessionService } from './inlineChatSessionService.js';
|
||||
import { InlineChatEnabler, InlineChatEscapeToolContribution, InlineChatSessionServiceImpl } from './inlineChatSessionServiceImpl.js';
|
||||
import { IInlineChatHistoryService, InlineChatHistoryService } from './inlineChatHistoryService.js';
|
||||
import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';
|
||||
import { CancelAction, ChatSubmitAction } from '../../chat/browser/actions/chatExecuteActions.js';
|
||||
import { localize } from '../../../../nls.js';
|
||||
@@ -36,6 +37,7 @@ registerAction2(InlineChatActions.RephraseInlineChatSessionAction);
|
||||
// --- browser
|
||||
|
||||
registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed);
|
||||
registerSingleton(IInlineChatHistoryService, InlineChatHistoryService, InstantiationType.Delayed);
|
||||
|
||||
// --- MENU special ---
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { HistoryNavigator2 } from '../../../../base/common/history.js';
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
||||
|
||||
export const IInlineChatHistoryService = createDecorator<IInlineChatHistoryService>('IInlineChatHistoryService');
|
||||
|
||||
export interface IInlineChatHistoryService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
addToHistory(value: string): void;
|
||||
previousValue(): string | undefined;
|
||||
nextValue(): string | undefined;
|
||||
isAtEnd(): boolean;
|
||||
replaceLast(value: string): void;
|
||||
resetCursor(): void;
|
||||
}
|
||||
|
||||
const _storageKey = 'inlineChat.history';
|
||||
const _capacity = 50;
|
||||
|
||||
export class InlineChatHistoryService extends Disposable implements IInlineChatHistoryService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _history: HistoryNavigator2<string>;
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
) {
|
||||
super();
|
||||
|
||||
const raw = this._storageService.get(_storageKey, StorageScope.PROFILE);
|
||||
let entries: string[] = [''];
|
||||
if (raw) {
|
||||
try {
|
||||
const parsed: string[] = JSON.parse(raw);
|
||||
if (Array.isArray(parsed) && parsed.length > 0) {
|
||||
entries = parsed;
|
||||
// Ensure there's always an empty uncommitted entry at the end
|
||||
if (entries[entries.length - 1] !== '') {
|
||||
entries.push('');
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore invalid data
|
||||
}
|
||||
}
|
||||
|
||||
this._history = new HistoryNavigator2<string>(entries, _capacity);
|
||||
|
||||
this._store.add(this._storageService.onWillSaveState(() => {
|
||||
this._saveToStorage();
|
||||
}));
|
||||
}
|
||||
|
||||
private _saveToStorage(): void {
|
||||
const values = [...this._history].filter(v => v.length > 0);
|
||||
if (values.length === 0) {
|
||||
this._storageService.remove(_storageKey, StorageScope.PROFILE);
|
||||
} else {
|
||||
this._storageService.store(_storageKey, JSON.stringify(values), StorageScope.PROFILE, StorageTarget.USER);
|
||||
}
|
||||
}
|
||||
|
||||
addToHistory(value: string): void {
|
||||
this._history.replaceLast(value);
|
||||
this._history.add('');
|
||||
}
|
||||
|
||||
previousValue(): string | undefined {
|
||||
return this._history.previous();
|
||||
}
|
||||
|
||||
nextValue(): string | undefined {
|
||||
return this._history.next();
|
||||
}
|
||||
|
||||
isAtEnd(): boolean {
|
||||
return this._history.isAtEnd();
|
||||
}
|
||||
|
||||
replaceLast(value: string): void {
|
||||
this._history.replaceLast(value);
|
||||
}
|
||||
|
||||
resetCursor(): void {
|
||||
this._history.resetCursor();
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import { ActionBar, ActionsOrientation } from '../../../../base/browser/ui/actio
|
||||
import { BaseActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js';
|
||||
import { DomScrollableElement } from '../../../../base/browser/ui/scrollbar/scrollableElement.js';
|
||||
import { Codicon } from '../../../../base/common/codicons.js';
|
||||
import { HistoryNavigator2 } from '../../../../base/common/history.js';
|
||||
import { MarkdownString } from '../../../../base/common/htmlContent.js';
|
||||
import { KeyCode } from '../../../../base/common/keyCodes.js';
|
||||
import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
|
||||
@@ -42,6 +41,7 @@ import { getSimpleEditorOptions } from '../../codeEditor/browser/simpleEditorOpt
|
||||
import { PlaceholderTextContribution } from '../../../../editor/contrib/placeholderText/browser/placeholderTextContribution.js';
|
||||
import { IInlineChatSession2 } from './inlineChatSessionService.js';
|
||||
import { assertType } from '../../../../base/common/types.js';
|
||||
import { IInlineChatHistoryService } from './inlineChatHistoryService.js';
|
||||
|
||||
/**
|
||||
* Overlay widget that displays a vertical action bar menu.
|
||||
@@ -63,8 +63,6 @@ export class InlineChatInputWidget extends Disposable {
|
||||
private _anchorLeft: number = 0;
|
||||
private _anchorAbove: boolean = false;
|
||||
|
||||
private readonly _historyNavigator = new HistoryNavigator2<string>([''], 50);
|
||||
|
||||
constructor(
|
||||
private readonly _editorObs: ObservableCodeEditor,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@@ -72,6 +70,7 @@ export class InlineChatInputWidget extends Disposable {
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IInlineChatHistoryService private readonly _historyService: IInlineChatHistoryService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -237,7 +236,7 @@ export class InlineChatInputWidget extends Disposable {
|
||||
const model = this._input.getModel();
|
||||
const position = this._input.getPosition();
|
||||
if (position && position.lineNumber === model.getLineCount()) {
|
||||
if (!this._historyNavigator.isAtEnd()) {
|
||||
if (!this._historyService.isAtEnd()) {
|
||||
this._showNextHistoryValue();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -278,24 +277,27 @@ export class InlineChatInputWidget extends Disposable {
|
||||
}
|
||||
|
||||
addToHistory(value: string): void {
|
||||
this._historyNavigator.replaceLast(value);
|
||||
this._historyNavigator.add('');
|
||||
this._historyService.addToHistory(value);
|
||||
}
|
||||
|
||||
private _showPreviousHistoryValue(): void {
|
||||
if (this._historyNavigator.isAtEnd()) {
|
||||
this._historyNavigator.replaceLast(this._input.getModel().getValue());
|
||||
if (this._historyService.isAtEnd()) {
|
||||
this._historyService.replaceLast(this._input.getModel().getValue());
|
||||
}
|
||||
const value = this._historyService.previousValue();
|
||||
if (value !== undefined) {
|
||||
this._input.getModel().setValue(value);
|
||||
}
|
||||
const value = this._historyNavigator.previous();
|
||||
this._input.getModel().setValue(value);
|
||||
}
|
||||
|
||||
private _showNextHistoryValue(): void {
|
||||
if (this._historyNavigator.isAtEnd()) {
|
||||
if (this._historyService.isAtEnd()) {
|
||||
return;
|
||||
}
|
||||
const value = this._historyNavigator.next();
|
||||
this._input.getModel().setValue(value);
|
||||
const value = this._historyService.nextValue();
|
||||
if (value !== undefined) {
|
||||
this._input.getModel().setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +310,7 @@ export class InlineChatInputWidget extends Disposable {
|
||||
this._showStore.clear();
|
||||
|
||||
// Reset history cursor to the end (current uncommitted text)
|
||||
this._historyNavigator.resetCursor();
|
||||
this._historyService.resetCursor();
|
||||
|
||||
// Clear input state
|
||||
this._input.updateOptions({ wordWrap: 'off', placeholder });
|
||||
|
||||
Reference in New Issue
Block a user