Refactor trimming utility

master
Araozu 2024-07-05 09:09:49 -05:00
parent 5208496124
commit 631acc8122
4 changed files with 200 additions and 4 deletions

View File

@ -0,0 +1,7 @@
---
import { leftTrimDedent } from "./utils";
const { thpcode } = Astro.props;
---
<pre class="language-thp" data-language="thp"><code class="language-thp">{leftTrimDedent(thpcode).join("\n")}</code></pre>

View File

@ -0,0 +1,133 @@
import { expect, test } from 'vitest'
import { leftTrimDedent } from "./utils"
test("should trim empty string", () => {
const input = ``;
expect(leftTrimDedent(input)).toEqual([""]);
})
test("should work on a single line", () => {
const input = `hello`
expect(leftTrimDedent(input)).toEqual([
"hello"
]);
})
test("should trim a single line", () => {
const input = ` hello`
expect(leftTrimDedent(input)).toEqual([
"hello"
]);
})
test("should trim multiple lines", () => {
const input = ` hello\n world`
expect(leftTrimDedent(input)).toEqual([
"hello",
"world"
]);
})
test("should trim multiple lines without indentation", () => {
const input = `hello\nworld`
expect(leftTrimDedent(input)).toEqual([
"hello",
"world"
]);
})
test("should consume only whitespace", () => {
const input = ` hello\nworld`;
try {
const res = leftTrimDedent(input);
expect(res).not.toEqual([
"hello",
"rld",
]);
} catch (e) {
expect(e).toBeInstanceOf(Error);
expect(e).toHaveProperty("message", "Invalid indentation while trimming: Expected 2 spaces, got 0");
}
})
test("should preserve deeper indentation", () => {
const input = ` hello\n world`
expect(leftTrimDedent(input)).toEqual([
"hello",
" world",
]);
})
test("should ignore empty lines", () => {
const input = ` hello\n\n world`
expect(leftTrimDedent(input)).toEqual([
"hello",
"",
"world",
]);
})
test("should ignore lines with only whitespace", () => {
const input = ` hello\n \n \n world`
expect(leftTrimDedent(input)).toEqual([
"hello",
"",
" ",
"world",
]);
})
test("should trim multiple without indentation", () => {
const input = `hello\nworld\n!`
expect(leftTrimDedent(input)).toEqual([
"hello",
"world",
"!",
]);
})
test("should ignore empty first line", () => {
const input = `
hello
world`;
expect(leftTrimDedent(input)).toEqual([
"hello",
"world",
]);
})
test("should ignore empty first line 2", () => {
const input = `
hello
world`;
expect(leftTrimDedent(input)).toEqual([
"hello",
"",
"world",
]);
})
test("should ignore empty last line", () => {
const input = `
hello
world
`;
expect(leftTrimDedent(input)).toEqual([
"hello",
"world",
]);
});

View File

@ -45,7 +45,7 @@ export function trimAndDedent(input: string): Array<string> {
let currentIndentation = 0;
for (const c of characters) {
if (c === " ") { currentIndentation += 1 }
else {break;}
else { break; }
}
if (currentIndentation < indentationLevel) {
throw new Error("Invalid indentation while parsing THP code: " + lines[i]);
@ -67,4 +67,58 @@ export function trimAndDedent(input: string): Array<string> {
}
return lines;
}
}
export function leftTrimDedent(input: string): Array<string> {
let lines = input.split("\n");
let output: Array<string> = [];
// Ignore first line
if (lines[0] === "" && lines.length > 1) {
lines = lines.slice(1);
}
// Get indentation level of the first line
let indentationLevel = 0;
for (const char of lines[0]!) {
if (char === " ") {
indentationLevel += 1;
} else {
break;
}
}
for (const line of lines) {
// Ignore empty lines
if (line === "") {
output.push("");
continue;
}
output.push(trimWhitespace(line, indentationLevel));
}
if (output.length > 1 && output[output.length - 1] === "") {
output = output.slice(0, -1);
}
return output;
}
function trimWhitespace(input: string, count: number): string {
let indentCount = 0;
for (const char of input) {
if (char === " ") {
indentCount += 1;
} else {
break;
}
}
if (indentCount >= count || indentCount == input.length) {
return input.slice(count);
} else {
throw new Error(`Invalid indentation while trimming: Expected ${count} spaces, got ${indentCount}`);
}
}

View File

@ -2,6 +2,7 @@
layout: ../../../layouts/DocsLayout.astro
title: Introduction
---
import Code from "../../../components/Code.astro"
# THP templating
@ -71,7 +72,8 @@ fun Button(String name) -> Html {
It is very similar to React. The HTML is inside the THP code, not the other
way around, so you can have arbitrary logic in the component.
```thp
<Code thpcode={`
fun User(String name) {
// Get info from the database
val user = try Model::get_user(name)
@ -96,7 +98,7 @@ fun TransactionItem(Transaction t) {
{t.date} - {t.name} ({t.price})
</li>
}
```
`} />