feat: improve error rendering on code snippets
This commit is contained in:
parent
a476fffe2c
commit
25a5b20d5f
@ -4,5 +4,5 @@ const { error_type = "Unknown", error_message } = Astro.props;
|
|||||||
|
|
||||||
<div class="px-4 py-2 rounded bg-red-200 dark:bg-red-950">
|
<div class="px-4 py-2 rounded bg-red-200 dark:bg-red-950">
|
||||||
<span class="inline-block font-bold">{error_type} error:</span>
|
<span class="inline-block font-bold">{error_type} error:</span>
|
||||||
<span class="inline-block whitespace-pre-wrap">{error_message}</span>
|
<span class="whitespace-pre-wrap">{error_message}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
import { leftTrimDedent } from "../components/utils";
|
import { leftTrimDedent } from "../components/utils";
|
||||||
|
|
||||||
|
export type ReferenceItem = {
|
||||||
|
symbol_start: number
|
||||||
|
symbol_end: number
|
||||||
|
reference: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface Token {
|
export interface Token {
|
||||||
token_type: TokenType
|
token_type: TokenType
|
||||||
value: string
|
value: string
|
||||||
@ -55,13 +61,13 @@ export interface SemanticError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenizeResult {
|
export interface TokenizeResult {
|
||||||
Ok?: Token[],
|
Ok?: [Array<Token>, Array<ReferenceItem>],
|
||||||
SyntaxOnly?: [Token[], Err],
|
SyntaxOnly?: [Token[], Err],
|
||||||
TokensOnly?: [Token[], Err],
|
TokensOnly?: [Token[], Err],
|
||||||
Err?: Err,
|
Err?: Err,
|
||||||
}
|
}
|
||||||
|
|
||||||
const error_classes = "underline decoration-wavy decoration-red-500";
|
const error_classes = "underline underline-offset-4 decoration-wavy decoration-red-500";
|
||||||
|
|
||||||
export async function native_highlighter(code: string): Promise<[string, string, string | null]> {
|
export async function native_highlighter(code: string): Promise<[string, string, string | null]> {
|
||||||
let formatted_code = leftTrimDedent(code).join("\n");
|
let formatted_code = leftTrimDedent(code).join("\n");
|
||||||
@ -85,7 +91,12 @@ export async function native_highlighter(code: string): Promise<[string, string,
|
|||||||
return semantic_error_highlighter(formatted_code, tokens, error.Semantic!);
|
return semantic_error_highlighter(formatted_code, tokens, error.Semantic!);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokens = result.Ok!;
|
const tokens = result.Ok! as unknown as Array<Token>;
|
||||||
|
// TODO: this is disable because the compiler has not
|
||||||
|
// implemented this feature yet
|
||||||
|
// const [tokens, references] = result.Ok!;
|
||||||
|
// console.log("refs:");
|
||||||
|
// console.log(references);
|
||||||
|
|
||||||
const output = highlight_tokens(formatted_code, tokens);
|
const output = highlight_tokens(formatted_code, tokens);
|
||||||
|
|
||||||
@ -107,22 +118,25 @@ function lex_error_highlighter(code: string, error: LexError): [string, string,
|
|||||||
const token = `<span class="token ${error_classes}">${err_str}</span>`;
|
const token = `<span class="token ${error_classes}">${err_str}</span>`;
|
||||||
|
|
||||||
const all = `${before_err}${token}${after_err}`;
|
const all = `${before_err}${token}${after_err}`;
|
||||||
|
const [error_line, error_column] = absolute_to_line_column(code, error.position);
|
||||||
|
|
||||||
// TODO: Transform absolute posijion (error.position) into line:column
|
// TODO: Transform absolute posijion (error.position) into line:column
|
||||||
return [all, "Lexical", error.reason + " at position " + error.position]
|
return [all, "Lexical", error.reason + ` at line ${error_line}:${error_column} `]
|
||||||
}
|
}
|
||||||
|
|
||||||
function syntax_error_highlighter(code: string, tokens: Array<Token>, error: SyntaxError): [string, string, string] {
|
function syntax_error_highlighter(code: string, tokens: Array<Token>, error: SyntaxError): [string, string, string] {
|
||||||
const highlighted = highlight_tokens(code, tokens, error.error_start, error.error_end);
|
const highlighted = highlight_tokens(code, tokens, error.error_start, error.error_end);
|
||||||
|
const [error_line, error_column] = absolute_to_line_column(code, error.error_start);
|
||||||
|
|
||||||
const error_message = `${error.reason} from position ${error.error_start} to ${error.error_end}`;
|
const error_message = `${error.reason} at line ${error_line}:${error_column}`;
|
||||||
return [highlighted, "Syntax", error_message];
|
return [highlighted, "Syntax", error_message];
|
||||||
}
|
}
|
||||||
|
|
||||||
function semantic_error_highlighter(code: string, tokens: Array<Token>, error: SyntaxError): [string, string, string] {
|
function semantic_error_highlighter(code: string, tokens: Array<Token>, error: SyntaxError): [string, string, string] {
|
||||||
const highlighted = highlight_tokens(code, tokens, error.error_start, error.error_end);
|
const highlighted = highlight_tokens(code, tokens, error.error_start, error.error_end);
|
||||||
|
const [error_line, error_column] = absolute_to_line_column(code, error.error_start);
|
||||||
|
|
||||||
const error_message = `${error.reason} from position ${error.error_start} to ${error.error_end}`;
|
const error_message = `${error.reason} at line ${error_line}:${error_column}`;
|
||||||
return [highlighted, "Semantic", error_message];
|
return [highlighted, "Semantic", error_message];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +163,7 @@ function highlight_tokens(input: string, tokens: Array<Token>, error_start = -1,
|
|||||||
const token_start = t.position;
|
const token_start = t.position;
|
||||||
const token_end = t.position + t.value.length;
|
const token_end = t.position + t.value.length;
|
||||||
|
|
||||||
let is_errored = (token_start == error_start);
|
let is_errored = (token_start >= error_start && token_end <= error_end);
|
||||||
|
|
||||||
// Some tokens require processing (like multiline comments)
|
// Some tokens require processing (like multiline comments)
|
||||||
|
|
||||||
@ -172,6 +186,33 @@ function highlight_tokens(input: string, tokens: Array<Token>, error_start = -1,
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform an absolute position in source code to a line:column combination.
|
||||||
|
*
|
||||||
|
* Both line and column are 1-based
|
||||||
|
*
|
||||||
|
* @param input the source code
|
||||||
|
* @param absolute the absolute position
|
||||||
|
*/
|
||||||
|
function absolute_to_line_column(input: string, absolute: number): [number, number] {
|
||||||
|
let line_count = 1;
|
||||||
|
let last_newline_pos = 0;
|
||||||
|
|
||||||
|
// Count lines
|
||||||
|
for (let i = 0; i < input.length; i += 1) {
|
||||||
|
if (i === absolute) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input[i] === "\n") {
|
||||||
|
line_count += 1;
|
||||||
|
last_newline_pos = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [line_count, absolute - last_newline_pos];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Certain tokens store values that differ from the source code representation.
|
* Certain tokens store values that differ from the source code representation.
|
||||||
* For example, the multiline comment token stores the content of the comment
|
* For example, the multiline comment token stores the content of the comment
|
||||||
|
@ -30,6 +30,9 @@ Int char_code = 0b01000110
|
|||||||
Int not_octal = 032 // This is 32, not 26
|
Int not_octal = 032 // This is 32, not 26
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
// TODO: Make it a compile error to have leading zeroes,
|
||||||
|
and force users to use `0o` for octal
|
||||||
|
|
||||||
|
|
||||||
## Float
|
## Float
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ number /= 1
|
|||||||
number %= 2
|
number %= 2
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
**There are no prefix/postfix increment**, use `+=` or `-=`.
|
**There are no prefix/postfix increment operators** (`++`, `--`),
|
||||||
|
use `+=` or `-=` instead.
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
// Use
|
// Use
|
||||||
|
@ -64,9 +64,9 @@ val first = get_first_item(numbers)
|
|||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
() -> ()
|
() -> ()
|
||||||
() -> Int
|
() -> (Int)
|
||||||
(Int, Int) -> Int
|
(Int, Int) -> (Int)
|
||||||
[T](Array[T]) -> T
|
[T](Array[T]) -> (T)
|
||||||
`} />
|
`} />
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import Code from "../../../components/Code.astro"
|
|||||||
## Function as parameter
|
## Function as parameter
|
||||||
|
|
||||||
<Code thpcode={`
|
<Code thpcode={`
|
||||||
fun map[A, B](Array[A] input, (A) -> B function) -> Array[B]
|
fun map[A, B](Array[A] input, (A) -> (B) function) -> Array[B]
|
||||||
{
|
{
|
||||||
// implementation
|
// implementation
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user