Path: blob/main/extensions/copilot/src/extension/prompts/node/panel/currentSelection.tsx
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { AssistantMessage, BasePromptElementProps, PromptElement, PromptPiece, PromptSizing, UserMessage } from '@vscode/prompt-tsx';6import type { Range } from 'vscode';7import { TextDocumentSnapshot } from '../../../../platform/editing/common/textDocumentSnapshot';8import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';9import { ILogService } from '../../../../platform/log/common/logService';10import { ITabsAndEditorsService } from '../../../../platform/tabs/common/tabsAndEditorsService';11import * as path from '../../../../util/vs/base/common/path';12import { Location } from '../../../../vscodeTypes';13import { PromptReference } from '../../../prompt/common/conversation';14import { CurrentEditor } from './currentEditor';15import { CodeBlock } from './safeElements';1617interface CurrentSelectionProps extends BasePromptElementProps {18document?: TextDocumentSnapshot;19range?: Range;20includeFilepath?: boolean;21}2223interface CurrentSelectionState {24exceedsTokenBudget: boolean;25isIgnored: boolean;26}2728export class CurrentSelection extends PromptElement<CurrentSelectionProps, CurrentSelectionState> {2930constructor(31props: CurrentSelectionProps,32@IIgnoreService private readonly ignoreService: IIgnoreService,33@ILogService private readonly logger: ILogService,34@ITabsAndEditorsService private readonly _tabsAndEditorsService: ITabsAndEditorsService,35) {36super(props);37}3839override async prepare(sizing: PromptSizing): Promise<CurrentSelectionState> {40if (!this.props.document) {41return { isIgnored: false, exceedsTokenBudget: false };42}43const isIgnored = await this.ignoreService.isCopilotIgnored(this.props.document.uri);4445let exceedsTokenBudget = false;46const selection = CurrentSelection.getCurrentSelection(this._tabsAndEditorsService);47if (selection && (await sizing.countTokens(selection?.selectedText)) * 1.1 > sizing.tokenBudget) {48exceedsTokenBudget = true;49}5051return { isIgnored, exceedsTokenBudget };52}5354override render(state: CurrentSelectionState, sizing: PromptSizing): PromptPiece<any, any> | undefined {55const selection = CurrentSelection.getCurrentSelection(this._tabsAndEditorsService);56if (!selection) {57return <CurrentEditor />;58}5960const references = [new PromptReference(new Location(selection.activeDocument.uri, selection.range))];61const urisUsed = [selection.activeDocument.uri];6263if (state.isIgnored) {64return <ignoredFiles value={urisUsed} />;65}66if (state.exceedsTokenBudget) {67this.logger.info(`Dropped current selection (${sizing.tokenBudget} / ${sizing.endpoint.modelMaxPromptTokens} tokens)`);68return (<>69<AssistantMessage priority={this.props.priority} name='selection-too-large'>70Your active selection ({selection.fileName && <>{selection.selectedText.split('\n').length} lines from {path.basename(selection.fileName)}</>}) exceeded my maximum context size and was dropped. Please reduce the selection to the most relevant part.71</AssistantMessage>72</>);73}7475return (<>76<UserMessage priority={this.props.priority}>77Active selection:<br />78<br />79<br />80{selection.fileName && <>From the file: {path.basename(selection.fileName)}<br /></>}81<CodeBlock code={selection.selectedText} languageId={selection.languageId} uri={selection.activeDocument.uri} references={references} />82<br />83<br />84</UserMessage >85</>);86}8788static getCurrentSelection(tabsAndEditorsService: ITabsAndEditorsService, allowEmptySelection = false) {89const editor = tabsAndEditorsService.activeTextEditor;90const activeDocument = editor?.document;91if (activeDocument) {92const activeDocumentSelection = editor.selection;93if (activeDocumentSelection && (!activeDocumentSelection.isEmpty94|| activeDocumentSelection.isEmpty && allowEmptySelection)) {95const languageId = activeDocument.languageId;96const selectedText = activeDocument.getText(activeDocumentSelection);97return {98languageId,99selectedText,100activeDocument,101range: activeDocumentSelection,102fileName: activeDocument.fileName103};104}105}106}107}108109110