diff --git a/proposed-api-sample/package-lock.json b/proposed-api-sample/package-lock.json index 2d8cc941..81bda4a0 100644 --- a/proposed-api-sample/package-lock.json +++ b/proposed-api-sample/package-lock.json @@ -9,6 +9,9 @@ "version": "0.0.1", "hasInstallScript": true, "license": "MIT", + "dependencies": { + "@vscode/prompt-tsx": "^0.2.6-alpha" + }, "devDependencies": { "@types/node": "^18", "@typescript-eslint/eslint-plugin": "^7.14.0", @@ -385,6 +388,11 @@ "dts": "index.js" } }, + "node_modules/@vscode/prompt-tsx": { + "version": "0.2.6-alpha", + "resolved": "https://registry.npmjs.org/@vscode/prompt-tsx/-/prompt-tsx-0.2.6-alpha.tgz", + "integrity": "sha512-eijaM5kMFez6rHgSIivBvBBiYA9n0IeHXXinML74JqQV/lgCqoGNQ9Hlzh38P8KJLJ8aV8f14q6vPRIXbJfteg==" + }, "node_modules/acorn": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", @@ -1857,6 +1865,11 @@ "prompts": "^2.4.2" } }, + "@vscode/prompt-tsx": { + "version": "0.2.6-alpha", + "resolved": "https://registry.npmjs.org/@vscode/prompt-tsx/-/prompt-tsx-0.2.6-alpha.tgz", + "integrity": "sha512-eijaM5kMFez6rHgSIivBvBBiYA9n0IeHXXinML74JqQV/lgCqoGNQ9Hlzh38P8KJLJ8aV8f14q6vPRIXbJfteg==" + }, "acorn": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", diff --git a/proposed-api-sample/package.json b/proposed-api-sample/package.json index 322adca0..aedd9e63 100644 --- a/proposed-api-sample/package.json +++ b/proposed-api-sample/package.json @@ -1,5 +1,7 @@ { - "enabledApiProposals": [], + "enabledApiProposals": [ + "lmTools" + ], "name": "proposed-api-sample", "displayName": "proposed-api-sample", "description": "Sample showing how to use Proposed API", @@ -22,6 +24,12 @@ "command": "extension.helloWorld", "title": "Hello World" } + ], + "languageModelTools": [ + { + "name": "myTestTool", + "description": "My test tool" + } ] }, "scripts": { @@ -37,8 +45,11 @@ "@types/node": "^18", "@typescript-eslint/eslint-plugin": "^7.14.0", "@typescript-eslint/parser": "^7.14.0", + "@vscode/dts": "^0.4.0", "eslint": "^8.26.0", - "typescript": "^5.5.2", - "@vscode/dts": "^0.4.0" + "typescript": "^5.5.2" + }, + "dependencies": { + "@vscode/prompt-tsx": "^0.2.6-alpha" } } diff --git a/proposed-api-sample/src/asdf.tsx b/proposed-api-sample/src/asdf.tsx new file mode 100644 index 00000000..c6a10ea2 --- /dev/null +++ b/proposed-api-sample/src/asdf.tsx @@ -0,0 +1,38 @@ +import { BasePromptElementProps, PromptElement, PromptSizing, AssistantMessage, UserMessage, PromptPiece } from '@vscode/prompt-tsx'; +import * as vscode from 'vscode'; + +export interface PromptProps extends BasePromptElementProps { + userQuery: PromptPiece; +} + +export interface PromptState { + creationScript: string; +} + +export class TestPrompt extends PromptElement { + + render(state: PromptState, sizing: PromptSizing) { + return ( + <> + + Here are the creation scripts that were used to create the tables in my database. Pay close attention to the tables and columns that are available in my database:
+
+ {this.props.userQuery} + + ); + } +} + +export class MyCustomPrompt extends PromptElement { + render(state: void, sizing: PromptSizing): Promise | PromptPiece | undefined { + return + You are a SQL expert.
+ Your task is to help the user craft SQL queries that perform their task.
+ You should suggest SQL queries that are performant and correct.
+ Return your suggested SQL query in a Markdown code block that begins with ```sql and ends with ```.
+
+ } + +} + +export const myPromptEl = ; \ No newline at end of file diff --git a/proposed-api-sample/src/extension.ts b/proposed-api-sample/src/extension.ts index f6728059..8cb256b7 100644 --- a/proposed-api-sample/src/extension.ts +++ b/proposed-api-sample/src/extension.ts @@ -1,4 +1,6 @@ +import { renderPrompt } from '@vscode/prompt-tsx'; import * as vscode from 'vscode'; +import { myPromptEl, TestPrompt } from './asdf'; export function activate(context: vscode.ExtensionContext) { console.log('Congratulations, your extension "proposed-api-sample" is now active!'); @@ -8,8 +10,40 @@ export function activate(context: vscode.ExtensionContext) { * Proposed API as defined in vscode.proposed..d.ts. */ - const disposable = vscode.commands.registerCommand('extension.helloWorld', () => { - vscode.window.showInformationMessage('Hello World!'); + vscode.lm.registerTool('myTestTool', { + invoke(parameters, token): Promise { + return Promise.resolve({ + 'application/json': { + foo: 'bar' + }, + 'mytype': myPromptEl, + toString() { + return 'hello world!' + } + }); + }, + }); + + const disposable = vscode.commands.registerCommand('extension.helloWorld', async () => { + const result = await vscode.lm.invokeTool('myTestTool', {}, new vscode.CancellationTokenSource().token); + + console.log(result); + + const rendered = await renderPrompt( + TestPrompt, + { userQuery: result['mytype'] }, + { modelMaxPromptTokens: 4096 }, + { + tokenLength(text, token) { + return text.split(' ').length; + }, + countMessageTokens(message) { + return message.content.split(' ').length; + } + }, + ); + + console.log(JSON.stringify( rendered, null, 2)); }); context.subscriptions.push(disposable); diff --git a/proposed-api-sample/tsconfig.json b/proposed-api-sample/tsconfig.json index 9a3f5254..707a1256 100644 --- a/proposed-api-sample/tsconfig.json +++ b/proposed-api-sample/tsconfig.json @@ -6,6 +6,10 @@ "outDir": "out", "sourceMap": true, "strict": true, + + "jsx": "react", + "jsxFactory": "vscpp", + "jsxFragmentFactory": "vscppf", "rootDir": "src" }, "exclude": ["node_modules", ".vscode-test"] diff --git a/proposed-api-sample/vscode.d.ts b/proposed-api-sample/vscode.d.ts index 8c22232e..9f109570 100644 --- a/proposed-api-sample/vscode.d.ts +++ b/proposed-api-sample/vscode.d.ts @@ -157,7 +157,7 @@ declare module 'vscode' { * that the returned object is *not* live and changes to the * document are not reflected. * - * @param line A line number in [0, lineCount). + * @param line A line number in `[0, lineCount)`. * @returns A {@link TextLine line}. */ lineAt(line: number): TextLine; @@ -4683,7 +4683,7 @@ declare module 'vscode' { /** * The currently active {@linkcode SignatureHelp}. * - * The `activeSignatureHelp` has its [`SignatureHelp.activeSignature`] field updated based on + * The `activeSignatureHelp` has its {@linkcode SignatureHelp.activeSignature activeSignature} field updated based on * the user arrowing through available signatures. */ readonly activeSignatureHelp: SignatureHelp | undefined; @@ -5356,22 +5356,22 @@ declare module 'vscode' { export class Color { /** - * The red component of this color in the range [0-1]. + * The red component of this color in the range `[0-1]`. */ readonly red: number; /** - * The green component of this color in the range [0-1]. + * The green component of this color in the range `[0-1]`. */ readonly green: number; /** - * The blue component of this color in the range [0-1]. + * The blue component of this color in the range `[0-1]`. */ readonly blue: number; /** - * The alpha component of this color in the range [0-1]. + * The alpha component of this color in the range `[0-1]`. */ readonly alpha: number; @@ -6279,7 +6279,9 @@ declare module 'vscode' { * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable * to provide a word definition that uses exclusion of known separators. * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): - * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + * ``` + * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + * ``` */ wordPattern?: RegExp; /** @@ -7329,6 +7331,18 @@ declare module 'vscode' { */ readonly state: TerminalState; + /** + * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered + * features for the terminal. This will always be `undefined` immediately after the terminal + * is created. Listen to {@link window.onDidChangeTerminalShellIntegration} to be notified + * when shell integration is activated for a terminal. + * + * Note that this object may remain undefined if shell integation never activates. For + * example Command Prompt does not support shell integration and a user's shell setup could + * conflict with the automatic shell integration activation. + */ + readonly shellIntegration: TerminalShellIntegration | undefined; + /** * Send text to the terminal. The text is written to the stdin of the underlying pty process * (shell) of the terminal. @@ -7422,6 +7436,309 @@ declare module 'vscode' { readonly isInteractedWith: boolean; } + /** + * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal. + */ + export interface TerminalShellIntegration { + /** + * The current working directory of the terminal. This {@link Uri} may represent a file on + * another machine (eg. ssh into another machine). This requires the shell integration to + * support working directory reporting. + */ + readonly cwd: Uri | undefined; + + /** + * Execute a command, sending ^C as necessary to interrupt any running command if needed. + * + * @param commandLine The command line to execute, this is the exact text that will be sent + * to the terminal. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidChangeTerminalShellIntegration(async ({ terminal, shellIntegration }) => { + * if (terminal === myTerm) { + * const execution = shellIntegration.executeCommand('echo "Hello world"'); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * console.log(`Command exited with code ${event.exitCode}`); + * } + * } + * })); + * // Fallback to sendText if there is no shell integration within 3 seconds of launching + * setTimeout(() => { + * if (!myTerm.shellIntegration) { + * myTerm.sendText('echo "Hello world"'); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * }, 3000); + * + * @example + * // Send command to terminal that has been alive for a while + * const commandLine = 'echo "Hello world"'; + * if (term.shellIntegration) { + * const execution = shellIntegration.executeCommand({ commandLine }); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * console.log(`Command exited with code ${event.exitCode}`); + * } + * } else { + * term.sendText(commandLine); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + */ + executeCommand(commandLine: string): TerminalShellExecution; + + /** + * Execute a command, sending ^C as necessary to interrupt any running command if needed. + * + * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) + * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to + * verify whether it was successful. + * + * @param command A command to run. + * @param args Arguments to launch the executable with which will be automatically escaped + * based on the executable type. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => { + * if (terminal === myTerm) { + * const command = shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } + * })); + * // Fallback to sendText if there is no shell integration within 3 seconds of launching + * setTimeout(() => { + * if (!myTerm.shellIntegration) { + * myTerm.sendText('echo "Hello world"'); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * }, 3000); + * + * @example + * // Send command to terminal that has been alive for a while + * const commandLine = 'echo "Hello world"'; + * if (term.shellIntegration) { + * const command = term.shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } else { + * term.sendText(commandLine); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + */ + executeCommand(executable: string, args: string[]): TerminalShellExecution; + } + + /** + * A command that was executed in a terminal. + */ + export interface TerminalShellExecution { + /** + * The command line that was executed. The {@link TerminalShellExecutionCommandLineConfidence confidence} + * of this value depends on the specific shell's shell integration implementation. This + * value may become more accurate after {@link window.onDidEndTerminalShellExecution} is + * fired. + * + * @example + * // Log the details of the command line on start and end + * window.onDidStartTerminalShellExecution(event => { + * const commandLine = event.execution.commandLine; + * console.log(`Command started\n${summarizeCommandLine(commandLine)}`); + * }); + * window.onDidEndTerminalShellExecution(event => { + * const commandLine = event.execution.commandLine; + * console.log(`Command ended\n${summarizeCommandLine(commandLine)}`); + * }); + * function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) { + * return [ + * ` Command line: ${command.commandLine.value}`, + * ` Confidence: ${command.commandLine.confidence}`, + * ` Trusted: ${command.commandLine.isTrusted} + * ].join('\n'); + * } + */ + readonly commandLine: TerminalShellExecutionCommandLine; + + /** + * The working directory that was reported by the shell when this command executed. This + * {@link Uri} may represent a file on another machine (eg. ssh into another machine). This + * requires the shell integration to support working directory reporting. + */ + readonly cwd: Uri | undefined; + + /** + * Creates a stream of raw data (including escape sequences) that is written to the + * terminal. This will only include data that was written after `read` was called for + * the first time, ie. you must call `read` immediately after the command is executed via + * {@link TerminalShellIntegration.executeCommand} or + * {@link window.onDidStartTerminalShellExecution} to not miss any data. + * + * @example + * // Log all data written to the terminal for a command + * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); + * const stream = command.read(); + * for await (const data of stream) { + * console.log(data); + * } + */ + read(): AsyncIterable; + } + + /** + * A command line that was executed in a terminal. + */ + export interface TerminalShellExecutionCommandLine { + /** + * The full command line that was executed, including both the command and its arguments. + */ + readonly value: string; + + /** + * Whether the command line value came from a trusted source and is therefore safe to + * execute without user additional confirmation, such as a notification that asks "Do you + * want to execute (command)?". This verification is likely only needed if you are going to + * execute the command again. + * + * This is `true` only when the command line was reported explicitly by the shell + * integration script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence}) + * and it used a nonce for verification. + */ + readonly isTrusted: boolean; + + /** + * The confidence of the command line value which is determined by how the value was + * obtained. This depends upon the implementation of the shell integration script. + */ + readonly confidence: TerminalShellExecutionCommandLineConfidence; + } + + /** + * The confidence of a {@link TerminalShellExecutionCommandLine} value. + */ + enum TerminalShellExecutionCommandLineConfidence { + /** + * The command line value confidence is low. This means that the value was read from the + * terminal buffer using markers reported by the shell integration script. Additionally one + * of the following conditions will be met: + * + * - The command started on the very left-most column which is unusual, or + * - The command is multi-line which is more difficult to accurately detect due to line + * continuation characters and right prompts. + * - Command line markers were not reported by the shell integration script. + */ + Low = 0, + + /** + * The command line value confidence is medium. This means that the value was read from the + * terminal buffer using markers reported by the shell integration script. The command is + * single-line and does not start on the very left-most column (which is unusual). + */ + Medium = 1, + + /** + * The command line value confidence is high. This means that the value was explicitly sent + * from the shell integration script or the command was executed via the + * {@link TerminalShellIntegration.executeCommand} API. + */ + High = 2 + } + + /** + * An event signalling that a terminal's shell integration has changed. + */ + export interface TerminalShellIntegrationChangeEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + } + + /** + * An event signalling that an execution has started in a terminal. + */ + export interface TerminalShellExecutionStartEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + + /** + * The terminal shell execution that has ended. + */ + readonly execution: TerminalShellExecution; + } + + /** + * An event signalling that an execution has ended in a terminal. + */ + export interface TerminalShellExecutionEndEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + + /** + * The terminal shell execution that has ended. + */ + readonly execution: TerminalShellExecution; + + /** + * The exit code reported by the shell. + * + * Note that `undefined` means the shell either did not report an exit code (ie. the shell + * integration script is misbehaving) or the shell reported a command started before the command + * finished (eg. a sub-shell was opened). Generally this should not happen, depending on the use + * case, it may be best to treat this as a failure. + * + * @example + * const execution = shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * if (event.exitCode === undefined) { + * console.log('Command finished but exit code is unknown'); + * } else if (event.exitCode === 0) { + * console.log('Command succeeded'); + * } else { + * console.log('Command failed'); + * } + * } + * }); + */ + readonly exitCode: number | undefined; + } + /** * Provides information on a line in a terminal in order to provide links for it. */ @@ -10457,6 +10774,25 @@ declare module 'vscode' { */ export const onDidChangeTerminalState: Event; + /** + * Fires when shell integration activates or one of its properties changes in a terminal. + */ + export const onDidChangeTerminalShellIntegration: Event; + + /** + * This will be fired when a terminal command is started. This event will fire only when + * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is + * activated for the terminal. + */ + export const onDidStartTerminalShellExecution: Event; + + /** + * This will be fired when a terminal command is ended. This event will fire only when + * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is + * activated for the terminal. + */ + export const onDidEndTerminalShellExecution: Event; + /** * Represents the current window's state. */ @@ -11463,15 +11799,15 @@ declare module 'vscode' { /** * If true, then the element will be selected. */ - select?: boolean; + readonly select?: boolean; /** * If true, then the element will be focused. */ - focus?: boolean; + readonly focus?: boolean; /** * If true, then the element will be expanded. If a number is passed, then up to that number of levels of children will be expanded */ - expand?: boolean | number; + readonly expand?: boolean | number; }): Thenable; } @@ -12433,7 +12769,7 @@ declare module 'vscode' { buttons: readonly QuickInputButton[]; /** - * An event signaling when a button in the title bar was triggered. + * An event signaling when a top level button (buttons stored in {@link buttons}) was triggered. * This event does not fire for buttons on a {@link QuickPickItem}. */ readonly onDidTriggerButton: Event; @@ -16880,6 +17216,11 @@ declare module 'vscode' { * Note: you cannot use this option with any other options that prompt the user like {@link AuthenticationGetSessionOptions.createIfNone createIfNone}. */ silent?: boolean; + + /** + * The account that you would like to get a session for. This is passed down to the Authentication Provider to be used for creating the correct session. + */ + account?: AuthenticationSessionAccountInformation; } /** @@ -16940,6 +17281,18 @@ declare module 'vscode' { readonly changed: readonly AuthenticationSession[] | undefined; } + /** + * The options passed in to the {@link AuthenticationProvider.getSessions} and + * {@link AuthenticationProvider.createSession} call. + */ + export interface AuthenticationProviderSessionOptions { + /** + * The account that is being asked about. If this is passed in, the provider should + * attempt to return the sessions that are only related to this account. + */ + account?: AuthenticationSessionAccountInformation; + } + /** * A provider for performing authentication to a service. */ @@ -16954,9 +17307,10 @@ declare module 'vscode' { * Get a list of sessions. * @param scopes An optional list of scopes. If provided, the sessions returned should match * these permissions, otherwise all sessions should be returned. + * @param options Additional options for getting sessions. * @returns A promise that resolves to an array of authentication sessions. */ - getSessions(scopes?: readonly string[]): Thenable; + getSessions(scopes: readonly string[] | undefined, options: AuthenticationProviderSessionOptions): Thenable; /** * Prompts a user to login. @@ -16969,9 +17323,10 @@ declare module 'vscode' { * then this should never be called if there is already an existing session matching these * scopes. * @param scopes A list of scopes, permissions, that the new session should be created with. + * @param options Additional options for creating a session. * @returns A promise that resolves to an authentication session. */ - createSession(scopes: readonly string[]): Thenable; + createSession(scopes: readonly string[], options: AuthenticationProviderSessionOptions): Thenable; /** * Removes the session corresponding to session id. @@ -17034,6 +17389,20 @@ declare module 'vscode' { */ export function getSession(providerId: string, scopes: readonly string[], options?: AuthenticationGetSessionOptions): Thenable; + /** + * Get all accounts that the user is logged in to for the specified provider. + * Use this paired with {@link getSession} in order to get an authentication session for a specific account. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * + * Note: Getting accounts does not imply that your extension has access to that account or its authentication sessions. You can verify access to the account by calling {@link getSession}. + * + * @param providerId The id of the provider to use + * @returns A thenable that resolves to a readonly array of authentication accounts. + */ + export function getAccounts(providerId: string): Thenable; + /** * An {@link Event} which fires when the authentication sessions of an authentication provider have * been added, removed, or changed. @@ -18618,8 +18987,8 @@ declare module 'vscode' { * An event that fires whenever feedback for a result is received, e.g. when a user up- or down-votes * a result. * - * The passed {@link ChatResultFeedback.result result} is guaranteed to be the same instance that was - * previously returned from this chat participant. + * The passed {@link ChatResultFeedback.result result} is guaranteed to have the same properties as the result that was + * previously returned from this chat participant's handler. */ onDidReceiveFeedback: Event; diff --git a/proposed-api-sample/vscode.proposed.findTextInFiles.d.ts b/proposed-api-sample/vscode.proposed.findTextInFiles.d.ts new file mode 100644 index 00000000..25e3ae0a --- /dev/null +++ b/proposed-api-sample/vscode.proposed.findTextInFiles.d.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/59924 + + /** + * Options that can be set on a findTextInFiles search. + */ + export interface FindTextInFilesOptions { + /** + * A {@link GlobPattern glob pattern} that defines the files to search for. The glob pattern + * will be matched against the file paths of files relative to their workspace. Use a {@link RelativePattern relative pattern} + * to restrict the search results to a {@link WorkspaceFolder workspace folder}. + */ + include?: GlobPattern; + + /** + * A {@link GlobPattern glob pattern} that defines files and folders to exclude. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. When `undefined`, default excludes will + * apply. + */ + exclude?: GlobPattern; + + /** + * Whether to use the default and user-configured excludes. Defaults to true. + */ + useDefaultExcludes?: boolean; + + /** + * The maximum number of results to search for + */ + maxResults?: number; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles?: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles?: boolean; + + /** + * Whether files in parent directories that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useParentIgnoreFiles"`. + */ + useParentIgnoreFiles?: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks?: boolean; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + export namespace workspace { + /** + * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + + /** + * Search text in files across all {@link workspace.workspaceFolders workspace folders} in the workspace. + * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. + * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. + * @param callback A callback, called for each result + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @return A thenable that resolves when the search is complete. + */ + export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; + } +} diff --git a/proposed-api-sample/vscode.proposed.lmTools.d.ts b/proposed-api-sample/vscode.proposed.lmTools.d.ts new file mode 100644 index 00000000..42711e0b --- /dev/null +++ b/proposed-api-sample/vscode.proposed.lmTools.d.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// version: 3 +// https://github.com/microsoft/vscode/issues/213274 + +declare module 'vscode' { + + // TODO@API capabilities + + export type JSONSchema = object; + + // API -> LM: an tool/function that is available to the language model + export interface LanguageModelChatFunction { + name: string; + description: string; + parametersSchema?: JSONSchema; + } + + // API -> LM: add tools as request option + export interface LanguageModelChatRequestOptions { + // TODO@API this will a heterogeneous array of different types of tools + tools?: LanguageModelChatFunction[]; + + /** + * Force a specific tool to be used. + */ + toolChoice?: string; + } + + // LM -> USER: function that should be used + export class LanguageModelChatResponseFunctionUsePart { + name: string; + parameters: any; + + constructor(name: string, parameters: any); + } + + // LM -> USER: text chunk + export class LanguageModelChatResponseTextPart { + value: string; + + constructor(value: string); + } + + export interface LanguageModelChatResponse { + + stream: AsyncIterable; + } + + + // USER -> LM: the result of a function call + export class LanguageModelChatMessageFunctionResultPart { + name: string; + content: string; + isError: boolean; + + constructor(name: string, content: string, isError?: boolean); + } + + export interface LanguageModelChatMessage { + content2: string | LanguageModelChatMessageFunctionResultPart; + } + + export interface LanguageModelToolResult { + /** + * The result can contain arbitrary representations of the content. An example might be 'prompt-tsx' to indicate an element that can be rendered with the @vscode/prompt-tsx library. + */ + [contentType: string]: any; + + /** + * A string representation of the result which can be incorporated back into an LLM prompt without any special handling. + */ + toString(): string; + } + + // Tool registration/invoking between extensions + + export namespace lm { + /** + * Register a LanguageModelTool. The tool must also be registered in the package.json `languageModelTools` contribution point. + */ + export function registerTool(name: string, tool: LanguageModelTool): Disposable; + + /** + * A list of all available tools. + */ + export const tools: ReadonlyArray; + + /** + * Invoke a tool with the given parameters. + * TODO@API Could request a set of contentTypes to be returned so they don't all need to be computed? + */ + export function invokeTool(name: string, parameters: Object, token: CancellationToken): Thenable; + } + + // Is the same as LanguageModelChatFunction now, but could have more details in the future + export interface LanguageModelToolDescription { + name: string; + description: string; + parametersSchema?: JSONSchema; + } + + export interface LanguageModelTool { + // TODO@API should it be LanguageModelToolResult | string? + invoke(parameters: any, token: CancellationToken): Thenable; + } +} diff --git a/proposed-api-sample/vscode.proposed.textSearchProvider.d.ts b/proposed-api-sample/vscode.proposed.textSearchProvider.d.ts new file mode 100644 index 00000000..5d4b2b50 --- /dev/null +++ b/proposed-api-sample/vscode.proposed.textSearchProvider.d.ts @@ -0,0 +1,281 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/59921 + + /** + * The parameters of a query for text search. + */ + export interface TextSearchQuery { + /** + * The text pattern to search for. + */ + pattern: string; + + /** + * Whether or not `pattern` should match multiple lines of text. + */ + isMultiline?: boolean; + + /** + * Whether or not `pattern` should be interpreted as a regular expression. + */ + isRegExp?: boolean; + + /** + * Whether or not the search should be case-sensitive. + */ + isCaseSensitive?: boolean; + + /** + * Whether or not to search for whole word matches only. + */ + isWordMatch?: boolean; + } + + /** + * A file glob pattern to match file paths against. + * TODO@roblourens merge this with the GlobPattern docs/definition in vscode.d.ts. + * @see {@link GlobPattern} + */ + export type GlobString = string; + + /** + * Options common to file and text search + */ + export interface SearchOptions { + /** + * The root folder to search within. + */ + folder: Uri; + + /** + * Files that match an `includes` glob pattern should be included in the search. + */ + includes: GlobString[]; + + /** + * Files that match an `excludes` glob pattern should be excluded from the search. + */ + excludes: GlobString[]; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles: boolean; + + /** + * Whether files in parent directories that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useParentIgnoreFiles"`. + */ + useParentIgnoreFiles: boolean; + } + + /** + * Options to specify the size of the result text preview. + * These options don't affect the size of the match itself, just the amount of preview text. + */ + export interface TextSearchPreviewOptions { + /** + * The maximum number of lines in the preview. + * Only search providers that support multiline search will ever return more than one line in the match. + */ + matchLines: number; + + /** + * The maximum number of characters included per line. + */ + charsPerLine: number; + } + + /** + * Options that apply to text search. + */ + export interface TextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Exclude files larger than `maxFileSize` in bytes. + */ + maxFileSize?: number; + + /** + * Interpret files using this encoding. + * See the vscode setting `"files.encoding"` + */ + encoding?: string; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + /** + * Represents the severity of a TextSearchComplete message. + */ + export enum TextSearchCompleteMessageType { + Information = 1, + Warning = 2, + } + + /** + * A message regarding a completed search. + */ + export interface TextSearchCompleteMessage { + /** + * Markdown text of the message. + */ + text: string; + /** + * Whether the source of the message is trusted, command links are disabled for untrusted message sources. + * Messaged are untrusted by default. + */ + trusted?: boolean; + /** + * The message type, this affects how the message will be rendered. + */ + type: TextSearchCompleteMessageType; + } + + /** + * Information collected when text search is complete. + */ + export interface TextSearchComplete { + /** + * Whether the search hit the limit on the maximum number of search results. + * `maxResults` on {@linkcode TextSearchOptions} specifies the max number of results. + * - If exactly that number of matches exist, this should be false. + * - If `maxResults` matches are returned and more exist, this should be true. + * - If search hits an internal limit which is less than `maxResults`, this should be true. + */ + limitHit?: boolean; + + /** + * Additional information regarding the state of the completed search. + * + * Messages with "Information" style support links in markdown syntax: + * - Click to [run a command](command:workbench.action.OpenQuickPick) + * - Click to [open a website](https://aka.ms) + * + * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again. + */ + message?: TextSearchCompleteMessage | TextSearchCompleteMessage[]; + } + + /** + * A preview of the text result. + */ + export interface TextSearchMatchPreview { + /** + * The matching lines of text, or a portion of the matching line that contains the match. + */ + text: string; + + /** + * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. + */ + matches: Range | Range[]; + } + + /** + * A match from a text search + */ + export interface TextSearchMatch { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * The range of the match within the document, or multiple ranges for multiple matches. + */ + ranges: Range | Range[]; + + /** + * A preview of the text match. + */ + preview: TextSearchMatchPreview; + } + + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; + + /** + * The line number of this line of context. + */ + lineNumber: number; + } + + export type TextSearchResult = TextSearchMatch | TextSearchContext; + + /** + * A TextSearchProvider provides search results for text results inside files in the workspace. + */ + export interface TextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameters for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; + } +}