Path: blob/main/extensions/copilot/src/extension/prompts/node/panel/binaryFileHexdump.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, PromptReference } from '@vscode/prompt-tsx';6import { IFileSystemService } from '../../../../platform/filesystem/common/fileSystemService';7import { formatHexdump, isBinaryContent } from '../../../../util/common/hexdump';8import { Schemas } from '../../../../util/vs/base/common/network';9import { Uri } from '../../../../vscodeTypes';10import { Tag } from '../base/tag';11import { CodeBlock } from './safeElements';1213export interface BinaryFileData {14readonly data: Uint8Array;15}1617export interface HexdumpIfBinaryOptions {18/** If provided, files already open as text documents are assumed to be text and skipped. */19readonly openTextDocuments?: readonly { readonly uri: Uri }[];20}2122// Known binary extensions that don't trip the usual nul-byte detection23const knownBinaryFileExtensions = new Set([24'.pdf',25]);2627/**28* Reads a file and returns its raw bytes if the content is binary.29* Returns `undefined` for text files, notebook cell URIs, or files already open30* as text documents, so callers can fall through to normal text handling.31*/32export async function hexdumpIfBinary(fileService: IFileSystemService, uri: Uri, options?: HexdumpIfBinaryOptions): Promise<BinaryFileData | undefined> {33if (uri.scheme === Schemas.vscodeNotebookCell || uri.scheme === Schemas.vscodeNotebookCellOutput) {34return undefined;35}3637if (options?.openTextDocuments?.some(doc => doc.uri.toString() === uri.toString())) {38return undefined;39}4041try {42const buffer = await fileService.readFile(uri);43const data = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);44const extDot = uri.path.lastIndexOf('.');45const ext = extDot >= 0 ? uri.path.substring(extDot).toLowerCase() : '';46if (isBinaryContent(data) || knownBinaryFileExtensions.has(ext)) {47return { data };48}49} catch {50// fall through51}52return undefined;53}5455export interface BinaryFileHexdumpProps extends BasePromptElementProps {56uri: Uri;57data: Uint8Array;58startByte?: number;59endByte?: number;60variableName?: string;61description?: string;62omitReferences?: boolean;63}6465const MAX_HEXDUMP_BYTES = 512;6667export class BinaryFileHexdump extends PromptElement<BinaryFileHexdumpProps> {68override async render() {69const { uri, data, startByte = 0, endByte = startByte + 128 } = this.props;70let start = startByte ?? 0;71let end = endByte ?? data.length;72if (start > end) {73[end, start] = [start, end];74}75start = Math.max(0, start);76end = Math.min(end, data.length, start + MAX_HEXDUMP_BYTES);7778const truncated = start !== (startByte ?? 0) || end !== (endByte ?? data.length);79const hexdump = formatHexdump(data, start, end - start);80const references = this.props.omitReferences ? undefined : [new PromptReference(this.props.variableName ? { variableName: this.props.variableName, value: uri } : uri)];8182return (83<Tag name='attachment' attrs={{ id: this.props.variableName, startByte: start, endByte: end, totalSize: data.length, truncated, description: this.props.description }}>84<CodeBlock uri={uri} references={references} code={hexdump} languageId='' fence='' />85</Tag>86);87}88}899091