Path: blob/main/extensions/copilot/src/extension/prompts/node/panel/currentEditor.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 { BasePromptElementProps, PromptElement, PromptSizing, UserMessage } from '@vscode/prompt-tsx';6import type { NotebookEditor, TextEditor } from 'vscode';7import { NotebookDocumentSnapshot } from '../../../../platform/editing/common/notebookDocumentSnapshot';8import { IIgnoreService } from '../../../../platform/ignore/common/ignoreService';9import { IAlternativeNotebookContentService } from '../../../../platform/notebook/common/alternativeContent';10import { ITabsAndEditorsService } from '../../../../platform/tabs/common/tabsAndEditorsService';11import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';12import { findCell, findNotebook } from '../../../../util/common/notebooks';13import { Schemas } from '../../../../util/vs/base/common/network';14import * as path from '../../../../util/vs/base/common/path';15import { Position, Range } from '../../../../vscodeTypes';16import { PromptReference } from '../../../prompt/common/conversation';17import { IPromptEndpoint } from '../base/promptRenderer';18import { CodeBlock } from './safeElements';1920export interface CurrentEditorPromptProps extends BasePromptElementProps {21}2223export class CurrentEditor extends PromptElement<CurrentEditorPromptProps> {24constructor(25props: CurrentEditorPromptProps,26@IIgnoreService private readonly _ignoreService: IIgnoreService,27@ITabsAndEditorsService private readonly _tabsAndEditorsService: ITabsAndEditorsService,28@IAlternativeNotebookContentService private readonly _alternativeNotebookContentService: IAlternativeNotebookContentService,29@IWorkspaceService private readonly _workspaceService: IWorkspaceService,30@IPromptEndpoint private readonly _promptEndpoint: IPromptEndpoint,31) {32super(props);33}3435async render(state: void, sizing: PromptSizing) {36const editor = this._tabsAndEditorsService.activeTextEditor;37if (editor) {38// TODO@DonJayamanne, need to figure out places relying on this and how its used.39// E.g. if problems were using this, then we need to translate positions in problems as well, & the like.40// return editor.document.uri.scheme === Schemas.vscodeNotebookCell ?41// this.renderActiveNotebookCellEditor(editor) :42// this.renderActiveTextEditor(editor);43return this.renderActiveTextEditor(editor);44}4546const notebookEditor = this._tabsAndEditorsService.activeNotebookEditor;47if (notebookEditor) {48return this.renderActiveNotebookEditor(notebookEditor);49}50return undefined;51}5253async renderActiveTextEditor(editor: TextEditor) {54const ranges = editor.visibleRanges;55const document = editor.document;5657const isIgnored = await this._ignoreService.isCopilotIgnored(document.uri);58if (isIgnored) {59return <ignoredFiles value={[document.uri]} />;60}6162if (document.getText().trim().length === 0) {63// The document is empty or contains only whitespace64return (<>65<UserMessage priority={this.props.priority}>66<references value={[new PromptReference(document.uri)]} />67The active {document.languageId} file {path.basename(document.uri.path)} is empty.68</UserMessage >69</>);70}7172if (ranges.length === 0) {73return undefined;74}7576return (<>77<UserMessage priority={this.props.priority}>78{ranges.map(range => (79<>80Excerpt from active file {path.basename(document.uri.path)}, lines {range.start.line + 1} to {range.end.line + 1}:<br />81<CodeBlock code={document.getText(range)} languageId={document.languageId} uri={document.uri} references={[new PromptReference({ uri: document.uri, range })]} />82<br />83<br />84</>85))}86</UserMessage >87</>);88}8990async renderActiveNotebookCellEditor(editor: TextEditor) {91if (editor.document.uri.scheme !== Schemas.vscodeNotebookCell) {92return;93}94const notebook = findNotebook(editor.document.uri, this._workspaceService.notebookDocuments);95const cellIndex = notebook && findCell(editor.document.uri, notebook)?.index;96if (!notebook || typeof cellIndex === 'undefined' || cellIndex < 0) {97return;98}99const format = this._alternativeNotebookContentService.getFormat(this._promptEndpoint);100const document = NotebookDocumentSnapshot.create(notebook, format);101const isIgnored = await this._ignoreService.isCopilotIgnored(document.uri);102if (isIgnored) {103return <ignoredFiles value={[document.uri]} />;104}105106if (document.getText().trim().length === 0) {107// The document is empty or contains only whitespace108return (<>109<UserMessage priority={this.props.priority}>110<references value={[new PromptReference(document.uri)]} />111The active {document.languageId} file {path.basename(document.uri.path)} is empty.112</UserMessage >113</>);114}115116if (editor.visibleRanges.length === 0) {117return undefined;118}119const altDocument = this._alternativeNotebookContentService.create(format).getAlternativeDocument(notebook);120const cell = notebook.cellAt(cellIndex);121const ranges = editor.visibleRanges.map(range => {122const start = altDocument.fromCellPosition(cell, range.start);123const end = altDocument.fromCellPosition(cell, range.end);124return new Range(start, end);125});126127return (<>128<UserMessage priority={this.props.priority}>129{ranges.map(range => (130<>131Excerpt from active file {path.basename(document.uri.path)}, lines {range.start.line + 1} to {range.end.line + 1}:<br />132<CodeBlock code={document.getText(range)} languageId={document.languageId} uri={document.uri} references={[new PromptReference({ uri: document.uri, range })]} />133<br />134<br />135</>136))}137</UserMessage >138</>);139}140141async renderActiveNotebookEditor(editor: NotebookEditor) {142const notebookRanges = editor.visibleRanges;143const format = this._alternativeNotebookContentService.getFormat(this._promptEndpoint);144const document = NotebookDocumentSnapshot.create(editor.notebook, format);145const isIgnored = await this._ignoreService.isCopilotIgnored(document.uri);146if (isIgnored) {147return <ignoredFiles value={[document.uri]} />;148}149150if (document.getText().trim().length === 0) {151// The document is empty or contains only whitespace152return (<>153<UserMessage priority={this.props.priority}>154<references value={[new PromptReference(document.uri)]} />155The active {document.languageId} file {path.basename(document.uri.path)} is empty.156</UserMessage >157</>);158}159160if (notebookRanges.length === 0) {161return undefined;162}163const altDocument = this._alternativeNotebookContentService.create(format).getAlternativeDocument(editor.notebook);164const ranges = notebookRanges.map(range => {165const cell = editor.notebook.cellAt(range.start);166const lastLine = cell.document.lineAt(cell.document.lineCount - 1);167const start = altDocument.fromCellPosition(cell, new Position(0, 0));168const end = altDocument.fromCellPosition(cell, lastLine.range.end);169return new Range(start, end);170});171172return (<>173<UserMessage priority={this.props.priority}>174{ranges.map(range => (175<>176Excerpt from active file {path.basename(document.uri.path)}, lines {range.start.line + 1} to {range.end.line + 1}:<br />177<CodeBlock code={document.getText(range)} languageId={document.languageId} uri={document.uri} references={[new PromptReference({ uri: document.uri, range })]} />178<br />179<br />180</>181))}182</UserMessage >183</>);184}185}186187188