Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
903 changes: 899 additions & 4 deletions vscode-wpilib/media/main.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion vscode-wpilib/resources/testing/riosender.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def handle(self):
while 1:
timestamp = time.time() - startTime
sequence = (sequence + 1) & 0xffff
split = self.makeErrorMsgSplit(timestamp, sequence, 1, 0x111111, 1, "this is an </br>errorwitha\r\nong text", "foo.c:1111", "traceback 1\ntraceback 2\ntraceback 3\n")
split = self.makeErrorMsgSplit(timestamp, sequence, 1, 0x111111, 1, "this is an error with a super long text. We want to know if the riolog will correctly wrap the content to the next line once it goes wayyyyyyyyyyyyyyyyyyyyyyy over the length limit.", "foo.c:1111", "traceback 1\ntraceback 2\ntraceback 3\n")
print(split[0])
print (split[1])
self.wfile.write(split[0])
Expand Down
212 changes: 212 additions & 0 deletions vscode-wpilib/src/riolog/ansi/ansiparser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
'use strict';

// ANSI color codes
export interface AnsiState {
foreground?: string;
background?: string;
bold?: boolean;
italic?: boolean;
underline?: boolean;
strikethrough?: boolean;
dim?: boolean;
}

// Color mapping for standard ANSI colors
const foregroundColors: { [key: number]: string } = {
30: '#000000', // black
31: '#f44336', // red
32: '#4caf50', // green
33: '#ffeb3b', // yellow
34: '#2196f3', // blue
35: '#e91e63', // magenta
36: '#00bcd4', // cyan
37: '#ffffff', // white
90: '#9e9e9e', // gray
91: '#ff5252', // lightred
92: '#8bc34a', // lightgreen
93: '#ffc107', // lightyellow
94: '#03a9f4', // lightblue
95: '#ec407a', // lightmagenta
96: '#26c6da', // lightcyan
97: '#fafafa', // white
};

const backgroundColors: { [key: number]: string } = {
40: '#000000', // black
41: '#f44336', // red
42: '#4caf50', // green
43: '#ffeb3b', // yellow
44: '#2196f3', // blue
45: '#e91e63', // magenta
46: '#00bcd4', // cyan
47: '#ffffff', // white
100: '#9e9e9e', // gray
101: '#ff5252', // lightred
102: '#8bc34a', // lightgreen
103: '#ffc107', // lightyellow
104: '#03a9f4', // lightblue
105: '#ec407a', // lightmagenta
106: '#26c6da', // lightcyan
107: '#fafafa', // white
};

export interface AnsiSegment {
text: string;
state: AnsiState;
}

// Parses a text string containing ANSI escape sequences and returns a collection of text segments with styling
export function parseAnsiString(text: string): AnsiSegment[] {
const result: AnsiSegment[] = [];
const regex = /\u001b\[((?:\d+;)*\d*)m/g;

let lastIndex = 0;
let match: RegExpExecArray | null;
let currentState: AnsiState = {};

while ((match = regex.exec(text)) !== null) {
// Add text before the escape sequence with current state
const segment = text.substring(lastIndex, match.index);
if (segment) {
result.push({
text: segment,
state: { ...currentState },
});
}

// Update state based on escape sequence
const codes = match[1].split(';').map((num) => parseInt(num || '0', 10));

// Process codes
for (let i = 0; i < codes.length; i++) {
const code = codes[i];

// Process reset and text styles
if (code === 0) {
// Reset all attributes
currentState = {};
} else if (code === 1) {
currentState.bold = true;
} else if (code === 2) {
currentState.dim = true;
} else if (code === 3) {
currentState.italic = true;
} else if (code === 4) {
currentState.underline = true;
} else if (code === 9) {
currentState.strikethrough = true;
} else if (code === 22) {
currentState.bold = false;
currentState.dim = false;
} else if (code === 23) {
currentState.italic = false;
} else if (code === 24) {
currentState.underline = false;
} else if (code === 29) {
currentState.strikethrough = false;
} else if (code === 39) {
delete currentState.foreground;
} else if (code === 49) {
delete currentState.background;
}
// Standard colors
else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) {
currentState.foreground = foregroundColors[code];
} else if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) {
currentState.background = backgroundColors[code];
}
// 8-bit color support (256 colors)
else if (code === 38 && i + 2 < codes.length && codes[i + 1] === 5) {
const colorCode = codes[i + 2];
// Generate 8-bit color
if (colorCode < 8) {
// Standard colors (0-7)
currentState.foreground = foregroundColors[colorCode + 30];
} else if (colorCode < 16) {
// High intensity colors (8-15)
currentState.foreground = foregroundColors[colorCode - 8 + 90];
} else if (colorCode < 232) {
// 216 colors (16-231): 6×6×6 cube
const r = Math.floor((colorCode - 16) / 36) * 51;
const g = Math.floor(((colorCode - 16) % 36) / 6) * 51;
const b = ((colorCode - 16) % 6) * 51;
currentState.foreground = `rgb(${r}, ${g}, ${b})`;
} else {
// Grayscale (232-255)
const gray = (colorCode - 232) * 10 + 8;
currentState.foreground = `rgb(${gray}, ${gray}, ${gray})`;
}
i += 2; // Skip the next two parameters
} else if (code === 48 && i + 2 < codes.length && codes[i + 1] === 5) {
const colorCode = codes[i + 2];
// Same logic for background colors
if (colorCode < 8) {
currentState.background = backgroundColors[colorCode + 40];
} else if (colorCode < 16) {
currentState.background = backgroundColors[colorCode - 8 + 100];
} else if (colorCode < 232) {
const r = Math.floor((colorCode - 16) / 36) * 51;
const g = Math.floor(((colorCode - 16) % 36) / 6) * 51;
const b = ((colorCode - 16) % 6) * 51;
currentState.background = `rgb(${r}, ${g}, ${b})`;
} else {
const gray = (colorCode - 232) * 10 + 8;
currentState.background = `rgb(${gray}, ${gray}, ${gray})`;
}
i += 2; // Skip the next two parameters
}
// 24-bit color support (RGB)
else if (code === 38 && i + 4 < codes.length && codes[i + 1] === 2) {
const r = codes[i + 2];
const g = codes[i + 3];
const b = codes[i + 4];
currentState.foreground = `rgb(${r}, ${g}, ${b})`;
i += 4; // Skip the next four parameters
} else if (code === 48 && i + 4 < codes.length && codes[i + 1] === 2) {
const r = codes[i + 2];
const g = codes[i + 3];
const b = codes[i + 4];
currentState.background = `rgb(${r}, ${g}, ${b})`;
i += 4; // Skip the next four parameters
}
}

lastIndex = match.index + match[0].length;
}

// Add the remaining text with current state
const remainingText = text.substring(lastIndex);
if (remainingText) {
result.push({
text: remainingText,
state: { ...currentState },
});
}

return result;
}

// Helper to apply ANSI styling to an HTML element
export function applyAnsiStyling(element: HTMLElement, state: AnsiState): void {
if (state.foreground) {
element.style.color = state.foreground;
}
if (state.background) {
element.style.backgroundColor = state.background;
}
if (state.bold) {
element.style.fontWeight = 'bold';
}
if (state.dim) {
element.style.opacity = '0.7';
}
if (state.italic) {
element.style.fontStyle = 'italic';
}
if (state.underline) {
element.style.textDecoration = (element.style.textDecoration || '') + ' underline';
}
if (state.strikethrough) {
element.style.textDecoration = (element.style.textDecoration || '') + ' line-through';
}
}
4 changes: 2 additions & 2 deletions vscode-wpilib/src/riolog/promisecond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ export class PromiseCondition {
this.condSet = () => {
resolve();
};
if (this.hasBeenSet === true) {
if (this.hasBeenSet) {
resolve();
}
});
}

public set() {
this.hasBeenSet = true;
if (this.condSet !== undefined) {
if (this.condSet) {
this.condSet();
}
}
Expand Down
4 changes: 1 addition & 3 deletions vscode-wpilib/src/riolog/rioconnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ function timerPromise(ms: number): ICancellableTimer {
let timer: NodeJS.Timeout | undefined;
return {
promise: new Promise((resolve, _) => {
timer = setTimeout(() => {
resolve(undefined);
}, ms);
timer = setTimeout(() => resolve(undefined), ms);
}),
cancel() {
if (timer === undefined) {
Expand Down
44 changes: 16 additions & 28 deletions vscode-wpilib/src/riolog/riologwindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class RioLogWindow {
return;
}
this.webview.on('didDispose', () => {
if (this.rioConsole !== undefined) {
if (this.rioConsole) {
this.rioConsole.stop();
this.rioConsole.removeAllListeners();
}
Expand All @@ -65,7 +65,7 @@ export class RioLogWindow {
}

public stop() {
if (this.webview !== undefined) {
if (this.webview) {
this.webview.dispose();
}
}
Expand All @@ -79,27 +79,22 @@ export class RioLogWindow {

private createWebView() {
this.webview = this.windowProvider.createWindowView();

this.webview.on('windowActive', async () => {
if (this.webview === undefined) {
return;
}

// Window goes active.
await this.webview.postMessage({
message: this.hiddenArray,
type: SendTypes.Batch,
});
if (this.rioConsole !== undefined) {
if (this.rioConsole.connected === true) {
await this.webview.postMessage({
message: true,
type: SendTypes.ConnectionChanged,
});
} else {
await this.webview.postMessage({
message: false,
type: SendTypes.ConnectionChanged,
});
}
if (this.rioConsole) {
await this.webview.postMessage({
message: true,
type: SendTypes.ConnectionChanged,
});
}
});
}
Expand All @@ -126,24 +121,17 @@ export class RioLogWindow {
if (this.webview === undefined) {
return;
}
if (connected) {
await this.webview.postMessage({
message: true,
type: SendTypes.ConnectionChanged,
});
} else {
await this.webview.postMessage({
message: false,
type: SendTypes.ConnectionChanged,
});
}
await this.webview.postMessage({
message: connected,
type: SendTypes.ConnectionChanged,
});
}

private async onNewMessageToSend(message: IPrintMessage | IErrorMessage) {
if (this.webview === undefined) {
return;
}
if (this.paused === true) {
if (this.paused) {
this.pausedArray.push(message);
await this.webview.postMessage({
message: this.pausedArray.length,
Expand All @@ -169,7 +157,7 @@ export class RioLogWindow {
} else if (data.type === ReceiveTypes.Pause) {
const old = this.paused;
this.paused = data.message as boolean;
if (old === true && this.paused === false) {
if (old && !this.paused) {
await this.sendPaused();
}
} else if (data.type === ReceiveTypes.Save) {
Expand All @@ -185,7 +173,7 @@ export class RioLogWindow {
} else if (data.type === ReceiveTypes.Reconnect) {
const newValue = data.message as boolean;
this.rioConsole.setAutoReconnect(newValue);
if (newValue === false) {
if (!newValue) {
this.rioConsole.disconnect();
}
} else if (data.type === ReceiveTypes.ChangeNumber) {
Expand Down
Loading
Loading