+1
-1
.github/workflows/test.yml
+1
-1
.github/workflows/test.yml
+10
CHANGELOG.md
+10
CHANGELOG.md
···
1
# Changelog
2
3
+
## v2.2.0 - 2025-11-04
4
+
5
+
- Updated the JavaScript FFI code to use the new API.
6
+
7
+
- `pearl` now requires Gleam v1.13 or higher.
8
+
9
+
## v2.1.0 - 2025-09-01
10
+
11
+
- Improved the performance of lexing comments and escape sequences.
12
+
13
## v2.0.0 - 2025-08-22
14
15
- Merged the `pearl/token` and `pearl/highlight` modules into one `pearl` module.
+6
-7
README.md
+6
-7
README.md
···
5
[](https://hex.pm/packages/pearl)
6
[](https://hexdocs.pm/pearl/)
7
8
-
Pearl is a lexer and syntax highlighter for Erlang, written in Gleam.
9
-
The `pearl` module, based on [`glexer`](https://hexdocs.mp/glexer) and
10
-
[`just`](https://hexdocs.mp/just), exposes a
11
-
standard lexer API, allowing you to convert Erlang source code into tokens.
12
-
The `pearl/highlight` module allows you to highlight Erlang code using ansi
13
-
colours, html or a custom format. Heavily inspired by [`contour`](https://hexdocs.pm/contour).
14
15
```sh
16
-
gleam add pearl@1
17
```
18
19
```gleam
···
5
[](https://hex.pm/packages/pearl)
6
[](https://hexdocs.pm/pearl/)
7
8
+
Pearl is a lexer and syntax highlighter for Erlang, written in Gleam. The lexer
9
+
is based on [`glexer`](https://hexdocs.pm/glexer) and [`just`](https://hexdocs.pm/just),
10
+
allowing you to convert Erlang source code into tokens. There is also an API
11
+
which allows you to highlight Erlang code using ansi colours, html or a custom
12
+
format. Heavily inspired by [`contour`](https://hexdocs.pm/contour).
13
14
```sh
15
+
gleam add pearl@2
16
```
17
18
```gleam
+7
-7
birdie_snapshots/highlight_comments_ansi.accepted
+7
-7
birdie_snapshots/highlight_comments_ansi.accepted
···
14
---
15
16
[0m
17
-
[0m[3m[90m%%% This a comment for the entire module[39m[23m[0m
18
-
19
-
[0m[3m[90m%% This is a doc comment for the main function[39m[23m[0m
20
-
[0m[34mmain[39m[0m([0m[0m)[0m[0m [0m[0m->[0m[0m
21
-
[0m[3m[90m% A comment within a function[39m[23m[0m
22
-
[0m[32mnil[39m[0m.[0m[0m [0m[3m[90m% This one is after the line[39m[23m[0m
23
-
[0m
···
14
---
15
16
[0m
17
+
[0m[3m[90m%%% This a comment for the entire module
18
+
[39m[23m[0m
19
+
[0m[3m[90m%% This is a doc comment for the main function
20
+
[39m[23m[34mmain[39m[0m([0m[0m)[0m[0m [0m[0m->[0m[0m
21
+
[0m[3m[90m% A comment within a function
22
+
[39m[23m[0m [0m[32mnil[39m[0m.[0m[0m [0m[3m[90m% This one is after the line
23
+
[39m[23m
+3
-2
gleam.toml
+3
-2
gleam.toml
···
1
name = "pearl"
2
-
version = "2.0.0"
3
4
description = "An Erlang lexer and syntax highlighter for Gleam!"
5
licences = ["Apache-2.0"]
···
10
11
[dependencies]
12
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
13
-
splitter = ">= 1.0.0 and < 2.0.0"
14
gleam_community_ansi = ">= 1.4.3 and < 2.0.0"
15
houdini = ">= 1.2.0 and < 2.0.0"
16
···
1
name = "pearl"
2
+
version = "2.2.0"
3
+
gleam = ">= 1.13.0"
4
5
description = "An Erlang lexer and syntax highlighter for Gleam!"
6
licences = ["Apache-2.0"]
···
11
12
[dependencies]
13
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
14
+
splitter = ">= 1.1.0 and < 2.0.0"
15
gleam_community_ansi = ">= 1.4.3 and < 2.0.0"
16
houdini = ">= 1.2.0 and < 2.0.0"
17
+2
-2
manifest.toml
+2
-2
manifest.toml
···
18
{ name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" },
19
{ name = "rank", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "rank", source = "hex", outer_checksum = "5660E361F0E49CBB714CC57CC4C89C63415D8986F05B2DA0C719D5642FAD91C9" },
20
{ name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" },
21
-
{ name = "splitter", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "128FC521EE33B0012E3E64D5B55168586BC1B9C8D7B0D0CA223B68B0D770A547" },
22
{ name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" },
23
{ name = "trie_again", version = "1.1.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "trie_again", source = "hex", outer_checksum = "365FE609649F3A098D1D7FC7EA5222EE422F0B3745587BF2AB03352357CA70BB" },
24
]
···
30
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
31
houdini = { version = ">= 1.2.0 and < 2.0.0" }
32
simplifile = { version = ">= 2.3.0 and < 3.0.0" }
33
-
splitter = { version = ">= 1.0.0 and < 2.0.0" }
···
18
{ name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" },
19
{ name = "rank", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "rank", source = "hex", outer_checksum = "5660E361F0E49CBB714CC57CC4C89C63415D8986F05B2DA0C719D5642FAD91C9" },
20
{ name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" },
21
+
{ name = "splitter", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "05564A381580395DCDEFF4F88A64B021E8DAFA6540AE99B4623962F52976AA9D" },
22
{ name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" },
23
{ name = "trie_again", version = "1.1.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "trie_again", source = "hex", outer_checksum = "365FE609649F3A098D1D7FC7EA5222EE422F0B3745587BF2AB03352357CA70BB" },
24
]
···
30
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
31
houdini = { version = ">= 1.2.0 and < 2.0.0" }
32
simplifile = { version = ">= 2.3.0 and < 3.0.0" }
33
+
splitter = { version = ">= 1.1.0 and < 2.0.0" }
+11
-12
src/pearl.gleam
+11
-12
src/pearl.gleam
···
628
| "^_" as sequence <> source
629
| "^?" as sequence <> source -> #(advance(lexer, source), sequence)
630
631
-
"x{" <> source -> lex_brace_escape_sequence(advance(lexer, source))
632
"x" <> source -> lex_hex_escape_sequence(advance(lexer, source))
633
634
"0" as char <> source
···
714
}
715
716
fn lex_brace_escape_sequence(lexer: Lexer) -> #(Lexer, String) {
717
-
case splitter.split(lexer.splitters.brace_escape_sequence, lexer.source) {
718
-
#(before, "}", after) -> #(advance(lexer, after), "x{" <> before <> "}")
719
-
#(before, separator, after) -> #(
720
-
advance(error(lexer, UnterminatedEscapeSequence), separator <> after),
721
-
"x{" <> before,
722
-
)
723
}
724
}
725
···
1300
}
1301
1302
fn lex_until_end_of_line(lexer: Lexer) -> #(Lexer, String) {
1303
-
let #(before, split, after) =
1304
-
splitter.split(lexer.splitters.until_end_of_line, lexer.source)
1305
-
#(advance(lexer, split <> after), before)
1306
}
1307
1308
fn lex_whitespace(lexer: Lexer, lexed: String) -> #(Lexer, Token) {
···
1418
/// pre code .hl-operator { color: #ffaff3 }
1419
/// pre code .hl-string { color: #c8ffa7 }
1420
/// pre code .hl-number { color: #c8ffa7 }
1421
-
/// pre code .hl-regexp { color: #c8ffa7 }
1422
-
/// pre code .hl-class { color: #ffddfa }
1423
/// ```
1424
///
1425
/// If you wish to use another format see `to_ansi` or `to_tokens`.
···
628
| "^_" as sequence <> source
629
| "^?" as sequence <> source -> #(advance(lexer, source), sequence)
630
631
+
"x{" <> _source -> lex_brace_escape_sequence(lexer)
632
"x" <> source -> lex_hex_escape_sequence(advance(lexer, source))
633
634
"0" as char <> source
···
714
}
715
716
fn lex_brace_escape_sequence(lexer: Lexer) -> #(Lexer, String) {
717
+
case
718
+
splitter.split_after(lexer.splitters.brace_escape_sequence, lexer.source)
719
+
{
720
+
#(before, "") -> #(error(lexer, UnterminatedEscapeSequence), before)
721
+
#(before, after) -> #(advance(lexer, after), before)
722
}
723
}
724
···
1299
}
1300
1301
fn lex_until_end_of_line(lexer: Lexer) -> #(Lexer, String) {
1302
+
let #(before, after) =
1303
+
splitter.split_after(lexer.splitters.until_end_of_line, lexer.source)
1304
+
#(advance(lexer, after), before)
1305
}
1306
1307
fn lex_whitespace(lexer: Lexer, lexed: String) -> #(Lexer, Token) {
···
1417
/// pre code .hl-operator { color: #ffaff3 }
1418
/// pre code .hl-string { color: #c8ffa7 }
1419
/// pre code .hl-number { color: #c8ffa7 }
1420
+
/// pre code .hl-atom { color: #c8ffa7 }
1421
+
/// pre code .hl-module { color: #ffddfa }
1422
/// ```
1423
///
1424
/// If you wish to use another format see `to_ansi` or `to_tokens`.
+3
-3
src/pearl_ffi.mjs
+3
-3
src/pearl_ffi.mjs
+1
-4
test/pearl_test.gleam
+1
-4
test/pearl_test.gleam