Path: blob/main/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts
3296 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 { URI } from '../../../../base/common/uri.js';6import { basename } from '../../../../base/common/path.js';7import { assert, assertNever } from '../../../../base/common/assert.js';89/**10* Base prompt parsing error class.11*/12abstract class ParseError extends Error {13/**14* Error type name.15*/16public readonly abstract errorType: string;1718constructor(19message?: string,20options?: ErrorOptions,21) {22super(message, options);23}2425/**26* Check if provided object is of the same type as this error.27*/28public sameTypeAs(other: unknown): other is typeof this {29if (other === null || other === undefined) {30return false;31}3233return other instanceof this.constructor;34}3536/**37* Check if provided object is equal to this error.38*/39public equal(other: unknown): boolean {40return this.sameTypeAs(other);41}42}4344/**45* Base resolve error class used when file reference resolution fails.46*/47export abstract class ResolveError extends ParseError {48public abstract override errorType: string;4950constructor(51public readonly uri: URI,52message?: string,53options?: ErrorOptions,54) {55super(message, options);56}57}5859/**60* A generic error for failing to resolve prompt contents stream.61*/62export class FailedToResolveContentsStream extends ResolveError {63public override errorType = 'FailedToResolveContentsStream';6465constructor(66uri: URI,67public readonly originalError: unknown,68message: string = `Failed to resolve prompt contents stream for '${uri.toString()}': ${originalError}.`,69) {70super(uri, message);71}72}737475/**76* Error that reflects the case when attempt to open target file fails.77*/78export class OpenFailed extends FailedToResolveContentsStream {79public override errorType = 'OpenError';8081constructor(82uri: URI,83originalError: unknown,84) {85super(86uri,87originalError,88`Failed to open '${uri.fsPath}': ${originalError}.`,89);90}91}9293/**94* Character use to join filenames/paths in a chain of references that95* lead to recursion.96*/97const DEFAULT_RECURSIVE_PATH_JOIN_CHAR = ' -> ';9899/**100* Error that reflects the case when attempt resolve nested file101* references failes due to a recursive reference, e.g.,102*103* ```markdown104* // a.md105* #file:b.md106* ```107*108* ```markdown109* // b.md110* #file:a.md111* ```112*/113export class RecursiveReference extends ResolveError {114public override errorType = 'RecursiveReferenceError';115116/**117* Cached default string representation of the recursive path.118*/119private defaultPathStringCache: string | undefined;120121constructor(122uri: URI,123public readonly recursivePath: readonly string[],124) {125// sanity check - a recursive path must always have at least126// two items in the list, otherwise it is not a recursive loop127assert(128recursivePath.length >= 2,129`Recursive path must contain at least two paths, got '${recursivePath.length}'.`,130);131132super(133uri, 'Recursive references found.',134);135}136137public override get message(): string {138return `${super.message} ${this.getRecursivePathString('fullpath')}`;139}140141/**142* Returns a string representation of the recursive path.143*/144public getRecursivePathString(145filename: 'basename' | 'fullpath',146pathJoinCharacter: string = DEFAULT_RECURSIVE_PATH_JOIN_CHAR,147): string {148const isDefault = (filename === 'fullpath') &&149(pathJoinCharacter === DEFAULT_RECURSIVE_PATH_JOIN_CHAR);150151if (isDefault && (this.defaultPathStringCache !== undefined)) {152return this.defaultPathStringCache;153}154155const result = this.recursivePath156.map((path) => {157if (filename === 'fullpath') {158return `'${path}'`;159}160161if (filename === 'basename') {162return `'${basename(path)}'`;163}164165assertNever(166filename,167`Unknown filename format '${filename}'.`,168);169})170.join(pathJoinCharacter);171172if (isDefault) {173this.defaultPathStringCache = result;174}175176return result;177}178179/**180* Check if provided object is of the same type as this181* error, contains the same recursive path and URI.182*/183public override equal(other: unknown): other is this {184if (!this.sameTypeAs(other)) {185return false;186}187188if (this.uri.toString() !== other.uri.toString()) {189return false;190}191192// performance optimization - compare number of paths in the193// recursive path chains first to avoid comparison of all strings194if (this.recursivePath.length !== other.recursivePath.length) {195return false;196}197198const myRecursivePath = this.getRecursivePathString('fullpath');199const theirRecursivePath = other.getRecursivePathString('fullpath');200201// performance optimization - if the path lengths don't match,202// no need to compare entire strings as they must be different203if (myRecursivePath.length !== theirRecursivePath.length) {204return false;205}206207return myRecursivePath === theirRecursivePath;208}209210/**211* Returns a string representation of the error object.212*/213public override toString(): string {214return `"${this.message}"(${this.uri})`;215}216}217218/**219* Error for the case when a resource URI doesn't point to a prompt file.220*/221export class NotPromptFile extends ResolveError {222public override errorType = 'NotPromptFileError';223224constructor(225uri: URI,226message: string = '',227) {228229const suffix = message ? `: ${message}` : '';230231super(232uri,233`Resource at ${uri.path} is not a prompt file${suffix}`,234);235}236}237238/**239* Error for the case when a resource URI points to a folder.240*/241export class FolderReference extends NotPromptFile {242public override errorType = 'FolderReferenceError';243244constructor(245uri: URI,246message: string = '',247) {248249const suffix = message ? `: ${message}` : '';250251super(252uri,253`Entity at '${uri.path}' is a folder${suffix}`,254);255}256}257258259