Add visual indicator to lexical errors in code snippets

This commit is contained in:
Araozu 2024-07-28 16:51:53 -05:00
parent 76aad9cd8a
commit 2fb095896b
5 changed files with 50 additions and 7 deletions

View File

@ -1,10 +1,17 @@
--- ---
import { native_highlighter } from "../lexer/highlighter"; import { native_highlighter } from "../lexer/highlighter";
import CodeError from "./docs/CodeError.astro";
const { thpcode } = Astro.props; const { thpcode } = Astro.props;
const native_html = await native_highlighter(thpcode); const [native_html, error_message] = await native_highlighter(thpcode);
--- ---
<pre <pre
class="language-thp"><code class="language-thp" set:html={native_html} /><span class="absolute top-2 right-2 inline-block text-sm select-none opacity-75">thp</span></pre> class="language-thp"><code class="language-thp" set:html={native_html} /><span class="absolute top-2 right-2 inline-block text-sm select-none opacity-75">thp
</span></pre>
{
error_message !== null && (
<CodeError error_type="Lexical">{error_message}</CodeError>
)
}

View File

@ -0,0 +1,8 @@
---
const { error_type = "Unknown" } = Astro.props;
---
<div class="px-4 py-2 rounded bg-red-200 dark:bg-red-950">
<span class="inline-block font-bold">{error_type} error:</span>
<slot />
</div>

View File

@ -45,13 +45,13 @@ export interface LexError {
} }
export async function native_highlighter(code: string): Promise<string> { export async function native_highlighter(code: string): Promise<[string, string | null]> {
let formatted_code = leftTrimDedent(code).join("\n"); let formatted_code = leftTrimDedent(code).join("\n");
const result = await native_lex(formatted_code); const result = await native_lex(formatted_code);
if (result.Err) { if (result.Err) {
throw new Error(JSON.stringify(result.Err.Lex) + "\n" + code); return lex_error_highlighter(formatted_code, result.Err!.Lex);
} }
const tokens = result.Ok!; const tokens = result.Ok!;
@ -82,7 +82,27 @@ export async function native_highlighter(code: string): Promise<string> {
current_pos = token_end; current_pos = token_end;
} }
return output; return [output, null];
}
/**
* Highlights code that has a lexic error
*/
function lex_error_highlighter(code: string, error: LexError): [string, string] {
// Create a single error token
const err_pos = error.position;
const before_err = code.substring(0, err_pos);
const err_str = code[err_pos];
const after_err = code.substring(err_pos + 1);
const token = `<span class="token underline decoration-wavy decoration-red-500">${err_str}</span>`;
const all = `${before_err}${token}${after_err}`;
// TODO: Transform absolute posijion (error.position) into line:column
return [all, error.reason + " at position " + error.position]
} }
function translate_token_type(tt: TokenType, value: string): string { function translate_token_type(tt: TokenType, value: string): string {

View File

@ -5,3 +5,11 @@ title: Readonly
import Code from "../../../components/Code.astro" import Code from "../../../components/Code.astro"
# Readonly # Readonly
<Code thpcode={`
class Caño
{
}
`} />

View File

@ -57,11 +57,11 @@ fun UserDetail(User user) -> HTML
{ {
<div> <div>
@match user.type @match user.type
case ::Admin @case ::Admin
{ {
<button>Delete resource</button> <button>Delete resource</button>
} }
case ::User @case ::User
{ {
<button disable>Not allowed</button> <button disable>Not allowed</button>
} }