Skip to content

Commit 0455fee

Browse files
committed
added support of #8000
1 parent 3531d06 commit 0455fee

File tree

4 files changed

+169
-1
lines changed

4 files changed

+169
-1
lines changed

vscode/src/lsp/listeners/requests/register.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,16 @@
1717
import { NbLanguageClient } from "../../nbLanguageClient"
1818
import { notificationOrRequestListenerType } from "../../types"
1919
import { requestListeners } from "./handlers"
20+
import { terminalListeners } from "./terminal"
21+
22+
23+
const listeners: notificationOrRequestListenerType[] = [
24+
...requestListeners,
25+
...terminalListeners
26+
];
2027

2128
export const registerRequestListeners = (client: NbLanguageClient) => {
22-
requestListeners.forEach((listener: notificationOrRequestListenerType) => {
29+
listeners.forEach((listener: notificationOrRequestListenerType) => {
2330
const { type, handler } = listener;
2431
client.onRequest(type, handler);
2532
})
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { LineBufferingPseudoterminal } from "../../../views/pseudoTerminal";
2+
import { CloseOutputRequest, ResetOutputRequest, ShowOutputRequest, WriteOutputRequest } from "../../protocol";
3+
import { notificationOrRequestListenerType } from "../../types";
4+
5+
const writeOutputRequestHandler = (param: any) => {
6+
const outputTerminal = LineBufferingPseudoterminal.getInstance(param.outputName)
7+
outputTerminal.acceptInput(param.message);
8+
}
9+
10+
const showOutputRequestHandler = (param: any) => {
11+
const outputTerminal = LineBufferingPseudoterminal.getInstance(param)
12+
outputTerminal.show();
13+
}
14+
15+
const closeOutputRequestHandler = (param: any) => {
16+
const outputTerminal = LineBufferingPseudoterminal.getInstance(param)
17+
outputTerminal.close();
18+
}
19+
20+
const resetOutputRequestHandler = (param: any) => {
21+
const outputTerminal = LineBufferingPseudoterminal.getInstance(param)
22+
outputTerminal.clear();
23+
}
24+
25+
26+
export const terminalListeners: notificationOrRequestListenerType[] = [{
27+
type: WriteOutputRequest.type,
28+
handler: writeOutputRequestHandler
29+
}, {
30+
type: ShowOutputRequest.type,
31+
handler: showOutputRequestHandler
32+
}, {
33+
type: CloseOutputRequest.type,
34+
handler: closeOutputRequestHandler
35+
}, {
36+
type: ResetOutputRequest.type,
37+
handler: resetOutputRequestHandler
38+
}];

vscode/src/lsp/protocol.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,25 @@ export function asRange(value: Range | undefined | null): vscode.Range | undefin
321321
export function asRanges(value: Range[]): vscode.Range[] {
322322
return value.map(value => asRange(value));
323323
}
324+
325+
export interface OutputMessage {
326+
outputName: string;
327+
message: string;
328+
stdIO: boolean;
329+
}
330+
331+
export namespace WriteOutputRequest {
332+
export const type = new ProtocolRequestType<OutputMessage, void, void, void, void>('output/write');
333+
}
334+
335+
export namespace ShowOutputRequest {
336+
export const type = new ProtocolRequestType<string, void, void, void, void>('output/show');
337+
}
338+
339+
export namespace CloseOutputRequest {
340+
export const type = new ProtocolRequestType<string, void, void, void, void>('output/close');
341+
}
342+
343+
export namespace ResetOutputRequest {
344+
export const type = new ProtocolRequestType<string, void, void, void, void>('output/reset');
345+
}

vscode/src/views/pseudoTerminal.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { Pseudoterminal, EventEmitter, Event, Terminal, window } from "vscode";
2+
3+
export class LineBufferingPseudoterminal implements Pseudoterminal {
4+
private static instances = new Map<string, LineBufferingPseudoterminal>();
5+
6+
private writeEmitter = new EventEmitter<string>();
7+
onDidWrite: Event<string> = this.writeEmitter.event;
8+
9+
private closeEmitter = new EventEmitter<void>();
10+
onDidClose?: Event<void> = this.closeEmitter.event;
11+
12+
private buffer: string = '';
13+
private isOpen = false;
14+
private readonly name: string;
15+
private terminal: Terminal | undefined;
16+
17+
private constructor(name: string) {
18+
this.name = name;
19+
}
20+
21+
open(): void {
22+
this.isOpen = true;
23+
}
24+
25+
close(): void {
26+
this.isOpen = false;
27+
this.closeEmitter.fire();
28+
}
29+
30+
/**
31+
* Accepts partial input strings and logs complete lines when they are formed.
32+
* Also processes carriage returns (\r) to overwrite the current line.
33+
* @param input The string input to the pseudoterminal.
34+
*/
35+
public acceptInput(input: string): void {
36+
if (!this.isOpen) {
37+
return;
38+
}
39+
40+
for (const char of input) {
41+
if (char === '\n') {
42+
// Process a newline: log the current buffer and reset it
43+
this.logLine(this.buffer.trim());
44+
this.buffer = '';
45+
} else if (char === '\r') {
46+
// Process a carriage return: log the current buffer on the same line
47+
this.logInline(this.buffer.trim());
48+
this.buffer = '';
49+
} else {
50+
// Append characters to the buffer
51+
this.buffer += char;
52+
}
53+
}
54+
}
55+
56+
private logLine(line: string): void {
57+
this.writeEmitter.fire(`${line}\r\n`);
58+
}
59+
60+
private logInline(line: string): void {
61+
// Clear the current line and move the cursor to the start
62+
this.writeEmitter.fire(`\x1b[2K\x1b[1G${line}`);
63+
}
64+
65+
public flushBuffer(): void {
66+
if (this.buffer.trim().length > 0) {
67+
this.logLine(this.buffer.trim());
68+
this.buffer = '';
69+
}
70+
}
71+
72+
public clear(): void {
73+
this.writeEmitter.fire('\x1b[2J\x1b[3J\x1b[H'); // Clear screen and move cursor to top-left
74+
}
75+
76+
public show(): void {
77+
if (!this.terminal) {
78+
this.terminal = window.createTerminal({
79+
name: this.name,
80+
pty: this,
81+
});
82+
}
83+
this.terminal.show(true);
84+
}
85+
86+
/**
87+
* Gets an existing instance or creates a new one by the terminal name.
88+
* The terminal is also created and managed internally.
89+
* @param name The name of the pseudoterminal.
90+
* @returns The instance of the pseudoterminal.
91+
*/
92+
public static getInstance(name: string): LineBufferingPseudoterminal {
93+
if (!this.instances.has(name)) {
94+
const instance = new LineBufferingPseudoterminal(name);
95+
this.instances.set(name, instance);
96+
}
97+
const instance = this.instances.get(name)!;
98+
instance.show();
99+
return instance;
100+
}
101+
}

0 commit comments

Comments
 (0)