Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/prompts/common/promptPathRepresentationService.ts
13401 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import type { Uri } from 'vscode';
7
import { createServiceIdentifier } from '../../../util/common/services';
8
import { getDriveLetter, hasDriveLetter } from '../../../util/vs/base/common/extpath';
9
import { Schemas } from '../../../util/vs/base/common/network';
10
import { isWindows } from '../../../util/vs/base/common/platform';
11
import { isDefined } from '../../../util/vs/base/common/types';
12
import { URI } from '../../../util/vs/base/common/uri';
13
import { IWorkspaceService } from '../../workspace/common/workspaceService';
14
15
export const IPromptPathRepresentationService = createServiceIdentifier<IPromptPathRepresentationService>('IPromptPathRepresentationService');
16
17
/**
18
* A service that is to be used to represent and restore document URI's in prompts.
19
* Using the service makes sure this happens in consistent and portable way across prompt elements.
20
*/
21
export interface IPromptPathRepresentationService {
22
23
_serviceBrand: undefined;
24
25
getFilePath(uri: Uri): string;
26
27
resolveFilePath(filePath: string, predominantScheme?: string): Uri | undefined;
28
29
getExampleFilePath(relativeFilePath: string): string;
30
}
31
32
/**
33
* Used to represent file URIs in prompts. Typically, this happens in code blocks using the `filepath` comment.
34
* Using the service makes sure this happens in a consistent and portable way across prompt elements.
35
* When creating a prompt, use `getFilePath` to get the string to use as a `filepath`.
36
* When readong a LLM response, use `resolveFilePath` to get the URI from from a `filepath`
37
*
38
* Do not use this service for other usages than prompts.
39
* We currently use the fsPath for local and remote filesystems, and URI.toString() for other schemes.
40
*/
41
export class PromptPathRepresentationService implements IPromptPathRepresentationService {
42
43
_serviceBrand: undefined;
44
45
protected isWindows() {
46
return isWindows;
47
}
48
49
constructor(@IWorkspaceService private readonly workspaceService: IWorkspaceService) { }
50
51
getFilePath(uri: Uri): string {
52
if (uri.scheme === Schemas.file || uri.scheme === Schemas.vscodeRemote) {
53
return uri.fsPath;
54
}
55
return uri.toString();
56
}
57
58
/**
59
* Resolves an `filepath` used in a prompt to a URI. The `filepath` should have been created by `getFilePath`.
60
*
61
* @param filepath The file path to resolve.
62
* @param predominantScheme The predominant scheme to use if the path is a file path. Defaults to 'file'.
63
*
64
* @returns The resolved URI or undefined if filepath does not look like a file path or URI.
65
*/
66
resolveFilePath(filepath: string, predominantScheme = Schemas.file): Uri | undefined {
67
// Always check for posix-like absolute paths, and also for platform-like
68
// (i.e. Windows) absolute paths in case the model generates them.
69
const isPosixPath = filepath.startsWith('/');
70
const isWindowsPath = this.isWindows() && (hasDriveLetter(filepath) || filepath.startsWith('\\'));
71
if (isPosixPath || isWindowsPath) {
72
// Some models double-escape backslashes, which causes problems down the line.
73
// Remove repeated backslashes from windows path (but preserve UNC paths)
74
if (isWindowsPath) {
75
const isUncPath = filepath.startsWith('\\\\');
76
filepath = filepath.replace(/\\+/g, '\\');
77
if (isUncPath) { filepath = '\\' + filepath; }
78
}
79
80
// Some models see an example of a unix path in tool calls and try to
81
// represent unix paths on windows without a drive letter, which causes
82
// issues. Try to rectify this.
83
if (isPosixPath && this.isWindows() && predominantScheme === Schemas.file) {
84
const lowerCandidates = this.workspaceService.getWorkspaceFolders()
85
.filter(folder => folder.scheme === Schemas.file)
86
.map(folder => getDriveLetter(folder.fsPath, true))
87
.filter(isDefined);
88
89
const matchingDriveLetter = lowerCandidates.find(c => this.workspaceService.getWorkspaceFolder(URI.file(`${c}:${filepath}`)));
90
if (matchingDriveLetter) {
91
filepath = `${matchingDriveLetter}:${filepath}`;
92
}
93
}
94
95
const fileUri = URI.file(filepath);
96
return predominantScheme === Schemas.file ? fileUri : URI.from({ scheme: predominantScheme, path: fileUri.path });
97
}
98
if (/\w[\w\d+.-]*:\S/.test(filepath)) { // starts with a scheme
99
try {
100
return URI.parse(filepath);
101
} catch (e) {
102
return undefined;
103
}
104
}
105
return undefined;
106
}
107
108
getExampleFilePath(absolutePosixFilePath: string): string {
109
if (this.isWindows()) {
110
return this.getFilePath(URI.parse(`file:///C:${absolutePosixFilePath}`));
111
} else {
112
return this.getFilePath(URI.parse(`file://${absolutePosixFilePath}`));
113
}
114
}
115
}
116
/**
117
* For testing we don't want OS dependent paths as they end up in the cache, so we use the posix path for all platforms.
118
*/
119
export class TestPromptPathRepresentationService extends PromptPathRepresentationService {
120
override getFilePath(uri: Uri): string {
121
if (uri.scheme === Schemas.file || uri.scheme === Schemas.vscodeRemote) {
122
return uri.path;
123
}
124
return uri.toString();
125
}
126
127
override getExampleFilePath(absolutePosixFilePath: string): string {
128
return this.getFilePath(URI.parse(`file://${absolutePosixFilePath}`));
129
}
130
}
131
132