Create shell-integration-sample

This commit is contained in:
Daniel Imms
2024-04-10 12:44:05 -07:00
parent 9f62ac5c90
commit cceb9cc6cf
16 changed files with 3688 additions and 0 deletions

View File

@ -0,0 +1,30 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "import",
"format": [ "camelCase", "PascalCase" ]
}
],
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": [
"out",
"dist",
"**/*.d.ts"
]
}

View File

@ -0,0 +1,5 @@
import { defineConfig } from '@vscode/test-cli';
export default defineConfig({
files: 'out/test/**/*.test.js',
});

View File

@ -0,0 +1,8 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint",
"ms-vscode.extension-test-runner"
]
}

View File

@ -0,0 +1,21 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
}
]
}

View File

@ -0,0 +1,11 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off"
}

View File

@ -0,0 +1,20 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@ -0,0 +1,11 @@
.vscode/**
.vscode-test/**
src/**
.gitignore
.yarnrc
vsc-extension-quickstart.md
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts
**/.vscode-test.*

View File

@ -0,0 +1,9 @@
# Change Log
All notable changes to the "shell-integration-sample" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

View File

@ -0,0 +1,71 @@
# shell-integration-sample README
This is the README for your extension "shell-integration-sample". After writing up a brief description, we recommend including the following sections.
## Features
Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file.
For example if there is an image subfolder under your extension project workspace:
\!\[feature X\]\(images/feature-x.png\)
> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow.
## Requirements
If you have any requirements or dependencies, add a section describing those and how to install and configure them.
## Extension Settings
Include if your extension adds any VS Code settings through the `contributes.configuration` extension point.
For example:
This extension contributes the following settings:
* `myExtension.enable`: Enable/disable this extension.
* `myExtension.thing`: Set to `blah` to do something.
## Known Issues
Calling out known issues can help limit users opening duplicate issues against your extension.
## Release Notes
Users appreciate release notes as you update your extension.
### 1.0.0
Initial release of ...
### 1.0.1
Fixed issue #.
### 1.1.0
Added features X, Y, and Z.
---
## Following extension guidelines
Ensure that you've read through the extensions guidelines and follow the best practices for creating your extension.
* [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines)
## Working with Markdown
You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts:
* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux).
* Toggle preview (`Shift+Cmd+V` on macOS or `Shift+Ctrl+V` on Windows and Linux).
* Press `Ctrl+Space` (Windows, Linux, macOS) to see a list of Markdown snippets.
## For more information
* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown)
* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/)
**Enjoy!**

3030
shell-integration-sample/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
{
"name": "shell-integration-sample",
"displayName": "shell-integration-sample",
"description": "",
"version": "0.0.1",
"engines": {
"vscode": "^1.88.0"
},
"enabledApiProposals": [
"terminalShellIntegration"
],
"categories": [
"Other"
],
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"terminal": {
"profiles": [
{
"id": "shell-integration-sample.track-editor-directory",
"title": "Track Editor Directory"
}
]
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"test": "vscode-test"
},
"devDependencies": {
"@types/vscode": "^1.88.0",
"@types/mocha": "^10.0.6",
"@types/node": "18.x",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"eslint": "^8.57.0",
"typescript": "^5.3.3",
"@vscode/test-cli": "^0.0.8",
"@vscode/test-electron": "^2.3.9"
}
}

View File

@ -0,0 +1,38 @@
import { basename, dirname } from 'path';
import * as vscode from 'vscode';
const terminalProfileName = 'Tracking Editor';
export function activate(context: vscode.ExtensionContext) {
const trackedTerminals: Set<vscode.Terminal> = new Set();
context.subscriptions.push(
vscode.window.registerTerminalProfileProvider('shell-integration-sample.track-editor-directory', {
provideTerminalProfile(token) {
return {
options: {
name: terminalProfileName
}
};
}
}),
vscode.window.onDidChangeTerminalShellIntegration(e => {
if (e.terminal.name === terminalProfileName && !trackedTerminals.has(e.terminal)) {
trackedTerminals.add(e.terminal);
}
}),
vscode.workspace.onDidOpenTextDocument(e => {
if (e.uri.scheme !== 'file') {
return;
}
const targetUri = vscode.Uri.file(dirname(e.fileName));
for (const terminal of trackedTerminals) {
const cwd = terminal.shellIntegration?.cwd;
if (cwd && cwd.toString() !== targetUri.toString()) {
terminal.shellIntegration.executeCommand('cd', [targetUri.fsPath]);
}
}
})
);
}

View File

@ -0,0 +1,15 @@
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../../extension';
suite('Extension Test Suite', () => {
vscode.window.showInformationMessage('Start all tests.');
test('Sample test', () => {
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
});
});

View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "Node16",
"target": "ES2022",
"outDir": "out",
"lib": [
"ES2022"
],
"sourceMap": true,
"rootDir": "src",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
}
}

View File

@ -0,0 +1,43 @@
# Welcome to your VS Code Extension
## What's in the folder
* This folder contains all of the files necessary for your extension.
* `package.json` - this is the manifest file in which you declare your extension and command.
* The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesnt yet need to load the plugin.
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
## Get up and running straight away
* Press `F5` to open a new window with your extension loaded.
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
* Find output from your extension in the debug console.
## Make changes
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
## Explore the API
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
## Run tests
* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered.
* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A`
* See the output of the test result in the Test Results view.
* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder.
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
* You can create folders inside the `test` folder to structure your tests any way you want.
## Go further
* [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns.
* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).

View File

@ -0,0 +1,313 @@
/*---------------------------------------------------------------------------------------------
* 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/145234
/**
* 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.ommandLine.value}`,
* ` Confidence: ${command.ommandLine.confidence}`,
* ` Trusted: ${command.ommandLine.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 `readData` was called for
* the first time, ie. you must call `readData` 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<string>;
}
/**
* 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
}
export interface Terminal {
/**
* 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.onDidActivateTerminalShellIntegration} 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;
}
/**
* [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.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
* if (terminal === myTerm) {
* const command = shellIntegration.executeCommand('echo "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({ commandLine });
* 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(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;
}
export interface TerminalShellIntegrationChangeEvent {
/**
* The terminal that shell integration has been activated in.
*/
readonly terminal: Terminal;
/**
* The shell integration object.
*/
readonly shellIntegration: TerminalShellIntegration;
}
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;
}
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. `undefined` means the shell did not report an exit
* code or the shell reported a command started before the command finished.
*/
readonly exitCode: number | undefined;
}
export namespace window {
/**
* Fires when shell integration activates or one of its properties changes in a terminal.
*/
export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent>;
/**
* 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<TerminalShellExecutionStartEvent>;
/**
* 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<TerminalShellExecutionEndEvent>;
}
}