mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-18 07:47:23 +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 { IWorkbenchContributionsRegistry, registerWorkbenchContribution2, Extensions as WorkbenchExtensions, WorkbenchPhase } from '../../../common/contributions.js';
|
||||||
import { IInlineChatSessionService } from './inlineChatSessionService.js';
|
import { IInlineChatSessionService } from './inlineChatSessionService.js';
|
||||||
import { InlineChatEnabler, InlineChatEscapeToolContribution, InlineChatSessionServiceImpl } from './inlineChatSessionServiceImpl.js';
|
import { InlineChatEnabler, InlineChatEscapeToolContribution, InlineChatSessionServiceImpl } from './inlineChatSessionServiceImpl.js';
|
||||||
|
import { IInlineChatHistoryService, InlineChatHistoryService } from './inlineChatHistoryService.js';
|
||||||
import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';
|
import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';
|
||||||
import { CancelAction, ChatSubmitAction } from '../../chat/browser/actions/chatExecuteActions.js';
|
import { CancelAction, ChatSubmitAction } from '../../chat/browser/actions/chatExecuteActions.js';
|
||||||
import { localize } from '../../../../nls.js';
|
import { localize } from '../../../../nls.js';
|
||||||
@@ -36,6 +37,7 @@ registerAction2(InlineChatActions.RephraseInlineChatSessionAction);
|
|||||||
// --- browser
|
// --- browser
|
||||||
|
|
||||||
registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed);
|
registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed);
|
||||||
|
registerSingleton(IInlineChatHistoryService, InlineChatHistoryService, InstantiationType.Delayed);
|
||||||
|
|
||||||
// --- MENU special ---
|
// --- 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 { BaseActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js';
|
||||||
import { DomScrollableElement } from '../../../../base/browser/ui/scrollbar/scrollableElement.js';
|
import { DomScrollableElement } from '../../../../base/browser/ui/scrollbar/scrollableElement.js';
|
||||||
import { Codicon } from '../../../../base/common/codicons.js';
|
import { Codicon } from '../../../../base/common/codicons.js';
|
||||||
import { HistoryNavigator2 } from '../../../../base/common/history.js';
|
|
||||||
import { MarkdownString } from '../../../../base/common/htmlContent.js';
|
import { MarkdownString } from '../../../../base/common/htmlContent.js';
|
||||||
import { KeyCode } from '../../../../base/common/keyCodes.js';
|
import { KeyCode } from '../../../../base/common/keyCodes.js';
|
||||||
import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.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 { PlaceholderTextContribution } from '../../../../editor/contrib/placeholderText/browser/placeholderTextContribution.js';
|
||||||
import { IInlineChatSession2 } from './inlineChatSessionService.js';
|
import { IInlineChatSession2 } from './inlineChatSessionService.js';
|
||||||
import { assertType } from '../../../../base/common/types.js';
|
import { assertType } from '../../../../base/common/types.js';
|
||||||
|
import { IInlineChatHistoryService } from './inlineChatHistoryService.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overlay widget that displays a vertical action bar menu.
|
* Overlay widget that displays a vertical action bar menu.
|
||||||
@@ -63,8 +63,6 @@ export class InlineChatInputWidget extends Disposable {
|
|||||||
private _anchorLeft: number = 0;
|
private _anchorLeft: number = 0;
|
||||||
private _anchorAbove: boolean = false;
|
private _anchorAbove: boolean = false;
|
||||||
|
|
||||||
private readonly _historyNavigator = new HistoryNavigator2<string>([''], 50);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _editorObs: ObservableCodeEditor,
|
private readonly _editorObs: ObservableCodeEditor,
|
||||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||||
@@ -72,6 +70,7 @@ export class InlineChatInputWidget extends Disposable {
|
|||||||
@IInstantiationService instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
@IModelService modelService: IModelService,
|
@IModelService modelService: IModelService,
|
||||||
@IConfigurationService configurationService: IConfigurationService,
|
@IConfigurationService configurationService: IConfigurationService,
|
||||||
|
@IInlineChatHistoryService private readonly _historyService: IInlineChatHistoryService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -237,7 +236,7 @@ export class InlineChatInputWidget extends Disposable {
|
|||||||
const model = this._input.getModel();
|
const model = this._input.getModel();
|
||||||
const position = this._input.getPosition();
|
const position = this._input.getPosition();
|
||||||
if (position && position.lineNumber === model.getLineCount()) {
|
if (position && position.lineNumber === model.getLineCount()) {
|
||||||
if (!this._historyNavigator.isAtEnd()) {
|
if (!this._historyService.isAtEnd()) {
|
||||||
this._showNextHistoryValue();
|
this._showNextHistoryValue();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -278,24 +277,27 @@ export class InlineChatInputWidget extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addToHistory(value: string): void {
|
addToHistory(value: string): void {
|
||||||
this._historyNavigator.replaceLast(value);
|
this._historyService.addToHistory(value);
|
||||||
this._historyNavigator.add('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showPreviousHistoryValue(): void {
|
private _showPreviousHistoryValue(): void {
|
||||||
if (this._historyNavigator.isAtEnd()) {
|
if (this._historyService.isAtEnd()) {
|
||||||
this._historyNavigator.replaceLast(this._input.getModel().getValue());
|
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 {
|
private _showNextHistoryValue(): void {
|
||||||
if (this._historyNavigator.isAtEnd()) {
|
if (this._historyService.isAtEnd()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const value = this._historyNavigator.next();
|
const value = this._historyService.nextValue();
|
||||||
this._input.getModel().setValue(value);
|
if (value !== undefined) {
|
||||||
|
this._input.getModel().setValue(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,7 +310,7 @@ export class InlineChatInputWidget extends Disposable {
|
|||||||
this._showStore.clear();
|
this._showStore.clear();
|
||||||
|
|
||||||
// Reset history cursor to the end (current uncommitted text)
|
// Reset history cursor to the end (current uncommitted text)
|
||||||
this._historyNavigator.resetCursor();
|
this._historyService.resetCursor();
|
||||||
|
|
||||||
// Clear input state
|
// Clear input state
|
||||||
this._input.updateOptions({ wordWrap: 'off', placeholder });
|
this._input.updateOptions({ wordWrap: 'off', placeholder });
|
||||||
|
|||||||
Reference in New Issue
Block a user