Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore terminal buffer API #207504

Open
Tracked by #22879
Tyriar opened this issue Mar 12, 2024 · 7 comments
Open
Tracked by #22879

Explore terminal buffer API #207504

Tyriar opened this issue Mar 12, 2024 · 7 comments
Assignees
Labels
api api-proposal feature-request Request for new features or functionality terminal Integrated terminal issues
Milestone

Comments

@Tyriar
Copy link
Member

Tyriar commented Mar 12, 2024

We may want to implement access to the terminal buffer before finalizing #145234. Copilot also has use for such an API.

@Tyriar Tyriar added feature-request Request for new features or functionality api terminal Integrated terminal issues api-proposal labels Mar 12, 2024
@Tyriar Tyriar added this to the March 2024 milestone Mar 12, 2024
@Tyriar Tyriar self-assigned this Mar 12, 2024
@Tyriar

This comment was marked as outdated.

@Tyriar

This comment was marked as outdated.

@Tyriar

This comment was marked as outdated.

@Tyriar

This comment was marked as outdated.

@Tyriar

This comment was marked as outdated.

@Tyriar
Copy link
Member Author

Tyriar commented Apr 12, 2024

Update:

/*---------------------------------------------------------------------------------------------
 *  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/207504
	//
	// The consequences of exposing this API will be that we must have a synchronous copy of the
	// terminal available on the extension host. This could be done in a couple of ways:
	//
	// - Send over diffs or serialized sections of the terminal periodically, this is probably
	//   harder to accomplish than it's worth.
	// - Run a parallel version of xterm.js on the extension host.
	//
	// The latter is already happening on both the renderer process and the pty host, so it's not a
	// huge deal, especially since there is a headless version of xterm.js and its memory layout is
	// quite compact; ~12 bytes per cell, so a fairly typical 160x20 viewport with 1000 scrollback
	// would use:
	//
	// 160*20*12 + 160*1000*12 bytes = 38400 + 1920000 bytes = ~1.87mb
	//
	// Similar to on the pty host this could have limited scrollback if we wanted to restrict memory
	// usage further, though that might be unexpected by extension authors and pose a challenge if
	// they want to read the entire output of a command.
	//
	// One of the niceties of doing this is that the extension host is close to the pty host, so
	// there is low latency in sending updates to the extension host, even on remote connections by
	// sending data between these processes ptyhost->server->exthost. If all data events are in
	// order then xterm.js will guarantee that they remain in sync.

	export interface Terminal {
		readonly buffer: TerminalBuffer;

		// TODO: Selection could be mutable, this could be done as a follow up.
		/**
		 * The selected range of the terminal or undefined if there is no selection. This range
		 * always refers to the {@link TerminalBufferSet.active active buffer}.
		 */
		readonly selection: TerminalBufferRange | undefined;

		// TODO: This is an alternative to onDidWriteTerminalData that aligns closer to
		//       shellIntegration proposal.
		/**
		 * A per-extension stream of raw data (including escape sequences) that is written to the
		 * terminal. This will only include data that was written after `dataStream` was called for
		 * the first time, ie. you must call `dataStream` immediately after the terminal is created
		 * via {@link createTerminal} or {@link onDidOpenTerminal} to not miss any data.
		 *
		 * @example
		 * // Log all data written to the terminal
		 * const term.createTerminal();
		 * for await (const data of term.read()) {
		 *   console.log(data);
		 * }
		 */
		read(): AsyncIterator<string>;
	}

	export interface TerminalSelection {
		buffer: TerminalBuffer;
		range: TerminalBufferRange;
	}

	export interface TerminalBuffer {
		/**
		 * The type of this buffer.
		 */
		readonly activeBufferType: TerminalBufferType;

		/**
		 * The number of lines that have been trimmed from the scrollback.
		 */
		readonly trimmedLineCount: number;

		/**
		 * The length of the buffer. This does not include trimmed lines.
		 */
		readonly length: number;

		// TODO: Can we omit cursor x, y, base y, viewport y?

		/**
		 * Returns a text line denoted by the line number. Note that the returned object is *not*
		 * live and changes to the buffer are not reflected.
		 *
		 * TODO: This could be in range [0, trimmedLineCount + length (exclusive)] if we just want
		 *       to pass the empty string for empty lines.
		 * @param line A line number in [trimmedLineCount, trimmedLineCount + length (exclusive)].
		 *
		 * @throws When the line number is not valid and/or the line has been trimmed from the
		 * buffer.
		 */
		lineAt(line: number): TerminalBufferLine;

		/**
		 * Get the text of this buffer. A substring can be retrieved by providing a range. The range
		 * will be {@link TerminalBuffer.validateRange adjusted}.
		 *
		 * @param range The range to get the text for. If not provided, the entire buffer's text
		 * will be returned.
		 */
		getText(range?: TerminalBufferRange): string;

		/**
		 * Ensure a range is completely contained in this terminal.
		 *
		 * @param range A range.
		 * @returns The given range or a new, adjusted range.
		 */
		validateRange(range: TerminalBufferRange): Range;
	}

	export enum TerminalBufferType {
		/**
		 * The normal buffer of a terminal. This is the buffer that is active when the terminal
		 * is first created and features scrollback.
		 */
		Normal,
		/**
		 * The alternate buffer of a terminal. This buffer is explicitly requested by the
		 * application running in the terminal and does not feature scrollback.
		 */
		Alternate
	}

	export interface TerminalBufferRange {
		// TODO: Could we just share Position here?
		start: Position;
		end: Position;
	}

	/**
	 * TerminalBufferLine objects are __immutable__. When a {@link TerminalBuffer buffer}'s content
	 * changes, previously retrieved lines will not represent the latest state.
	 */
	export interface TerminalBufferLine {
		/**
		 * The zero-based line number.
		 */
		readonly lineNumber: number;

		/**
		 * The text of this line without the line separator characters.
		 */
		readonly text: string;

		/**
		 * The range this line covers. This includes "whitespace" at the end of
		 * the line if the terminal cells were written to.
		 */
		readonly range: TerminalBufferRange;

		/**
		 * The offset of the first character which is not a whitespace character as defined
		 * by `/\s/`. **Note** that if a line is all whitespace the length of the line is returned.
		 */
		readonly firstNonWhitespaceCharacterIndex: number;

		/**
		 * Whether this line is whitespace only, shorthand
		 * for {@link TerminalBufferLine.firstNonWhitespaceCharacterIndex} === {@link TerminalBufferLine.text TerminalBufferLine.text.length}.
		 */
		readonly isEmptyOrWhitespace: boolean;
	}

	export namespace window {
		/**
		 * Fires when {@link TerminalBuffer.activeBufferType} changes.
		 */
		export const onDidChangeActiveTerminalBufferType: Event<TerminalActiveBufferTypeChangeEvent>;

		/**
		 * Fires when {@link Terminal.selection} changes.
		 */
		export const onDidChangeTerminalSelection: Event<TerminalSelectionChangeEvent>;
	}

	export interface TerminalActiveBufferTypeChangeEvent {
		readonly terminal: Terminal;
		readonly buffer: TerminalBuffer;
	}

	export interface TerminalSelectionChangeEvent {
		readonly terminal: Terminal;
		readonly selection: TerminalBufferRange | undefined;
	}

	export interface TerminalShellExecutionEndEvent {
		/**
		 * The output of the command when it has finished executing. This is the plain text shown in
		 * the terminal buffer and does not include raw escape sequences. Depending on the OS/shell
		 * setup, this may include the command line and/or prompt as part of the output.
		 *
		 *
		 */
		readonly output: TerminalBufferRange;
	}
}

@Tyriar Tyriar modified the milestones: April 2024, Backlog Apr 22, 2024
@Jessi-alt21
Copy link

#176812

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api api-proposal feature-request Request for new features or functionality terminal Integrated terminal issues
Projects
None yet
Development

No branches or pull requests

2 participants