antares 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +111 -0
- data/lib/antares/compatibility.rb +478 -0
- data/lib/antares/highlighter.rb +284 -0
- data/lib/antares/lexer_driver.rb +72 -0
- data/lib/antares/lexer_state_snapshot.rb +106 -0
- data/lib/antares/version.rb +5 -0
- data/lib/antares.rb +21 -0
- data/sig/antares.rbs +47 -0
- metadata +67 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a1968f47a0c8ea17a5e98fa2a3b6d4c339d2dcda58d4ae5032c7799d75ed17a6
|
|
4
|
+
data.tar.gz: 70e780331521d82f0dcbbe1ca6aa22de7f8ff7c2736eaca03a3e458ddb0e71f7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1ccfedd6fce3a1095aa81da6bd5f2285c7757a8dba9138eef211fdac309d5b5309e1b514c82e7fd593e93a6a851d9edded3d80c165176357cc7b13bfa43c7040
|
|
7
|
+
data.tar.gz: 5c18ad134cbf9fb791f625b3354a592b80d1052374fdf8f17e5c01b7917dd0cf73e4c2be863265d6372476776f769d31a3b2090757fce5eb9629eef0fb5ea7be
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yudai Takada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Antares
|
|
2
|
+
|
|
3
|
+
Incremental syntax highlighting for Rouge, with measured per-lexer compatibility.
|
|
4
|
+
The only runtime dependency is `rouge ~> 5.0`; document storage and colors belong
|
|
5
|
+
to the caller. Ruby 3.1 or later is supported.
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
require "antares"
|
|
9
|
+
|
|
10
|
+
lines = ["value = 1\n", "print(value)\n"]
|
|
11
|
+
highlighter = Antares::Highlighter.new(
|
|
12
|
+
lexer: Rouge::Lexers::Python.new,
|
|
13
|
+
lines: ->(index) { lines[index] },
|
|
14
|
+
line_count: -> { lines.length }
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
highlighter.tokens_for(0) # [[Rouge token class, UTF-8 text], ...]
|
|
18
|
+
lines[0] = "value = 2\n"
|
|
19
|
+
highlighter.edit(from_line: 0, removed: 1, inserted: 1)
|
|
20
|
+
highlighter.tokens_in(0..1) # one token array per line
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For a local checkout, `gem build antares.gemspec` creates an installable gem;
|
|
24
|
+
install that file with `gem install ./antares-0.1.0.gem`. Publication is separate
|
|
25
|
+
from building and is not performed by tests or CI.
|
|
26
|
+
|
|
27
|
+
## API and strategies
|
|
28
|
+
|
|
29
|
+
Line indices are zero based. Providers return one valid UTF-8 logical line each,
|
|
30
|
+
preferably including its newline. Missing separators are added to non-final
|
|
31
|
+
lines. A trailing empty logical line returns an empty token array. Tokens contain
|
|
32
|
+
text, not offsets: sum `text.bytesize` for byte offsets, or `text.length` for
|
|
33
|
+
character offsets. Returned rows/pairs/text are frozen.
|
|
34
|
+
|
|
35
|
+
Call `edit` **after** updating the provider, with the old removed line count and
|
|
36
|
+
new inserted line count. `frontier` is the first line not yet proven current;
|
|
37
|
+
`advance(until_line: 200)` lets an event loop schedule incremental progress.
|
|
38
|
+
`last_scanned_lines` reports actual lexical work, and `checkpoint_bytes` reports
|
|
39
|
+
normalized checkpoint payload bytes. Highlighter instances are not thread safe.
|
|
40
|
+
|
|
41
|
+
| Strategy | Behavior |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `:auto` | Uses the bundled, exact-Rouge-version compatibility verdict. Unknown versions and lexers use `:window`. |
|
|
44
|
+
| `:incremental` | Forces state snapshots and convergence. Use for certified lexers; forcing a failed lexer can return incorrect classifications. |
|
|
45
|
+
| `:window` | Restarts from root up to 200 lines before the requested range. Explicitly approximate for long-running constructs. |
|
|
46
|
+
| `:full` | Runs ordinary Rouge over the complete bounded document after every edit; use for exact results on small files with failed incremental lexers. |
|
|
47
|
+
|
|
48
|
+
`Antares.compatible?(Rouge::Lexers::Python)` returns `:incremental` or `:window`.
|
|
49
|
+
Every registered lexer has an entry in `COMPATIBILITY` and a reproducible result
|
|
50
|
+
in `COMPATIBILITY_DETAILS`; see the [complete compatibility table](docs/compatibility.md).
|
|
51
|
+
Do not infer support merely because a lexer subclasses `Rouge::RegexLexer`.
|
|
52
|
+
|
|
53
|
+
## State, limits, and correctness
|
|
54
|
+
|
|
55
|
+
Antares reuses Rouge's rule interpreter and deep-copies persistent instance
|
|
56
|
+
state, including nested arrays/hashes, their subclasses, delegate lexers, heredoc
|
|
57
|
+
queues, sets and structs. Immutable rule templates are shared. Per-call scanners
|
|
58
|
+
and callbacks are excluded. Dynamic rule closures compare conservatively by
|
|
59
|
+
identity, and opaque state is rejected rather than shallow-copied.
|
|
60
|
+
|
|
61
|
+
Checkpoints are placed approximately every 64 completed lines, **after entire
|
|
62
|
+
rule callbacks**. State fingerprints between checkpoints allow early convergence;
|
|
63
|
+
edits shift cached suffixes and invalidate stale boundaries. Calling
|
|
64
|
+
`continue_lex` separately for each line is insufficient: Rouge regexes may consume
|
|
65
|
+
several lines or inspect surrounding text. The driver therefore materializes a
|
|
66
|
+
bounded source String on demand, uses a fixed-anchor scanner, and resumes lexical
|
|
67
|
+
work at a checkpoint. It retains no array of provider lines. Source materialization
|
|
68
|
+
is O(document bytes) after an edit and is included in reported timings.
|
|
69
|
+
|
|
70
|
+
Some grammars are inherently nonlocal: for example, adding a distant `=end` makes
|
|
71
|
+
Rouge classify an earlier unmatched Ruby `=begin` differently. These lexers fail
|
|
72
|
+
the delayed-closure probes and select `:window`; the fallback is intentional.
|
|
73
|
+
Other failures are classified as token mismatch (lookahead/state dependencies) or
|
|
74
|
+
unsupported custom stream driver. The matrix is an empirical corpus guarantee,
|
|
75
|
+
not a proof for every possible input or non-default lexer option.
|
|
76
|
+
|
|
77
|
+
Default limits are 100,000 lines, an 8 MiB source snapshot, 16 KiB per line,
|
|
78
|
+
256 KiB per checkpoint, and 250 ms per scan. Configure them with `max_lines`,
|
|
79
|
+
`max_bytes`, `max_line_bytes`, `max_checkpoint_bytes`, and `max_seconds`.
|
|
80
|
+
Exceeding a source limit switches to window mode; oversized individual lines and
|
|
81
|
+
timed-out ranges return plain-text tokens without truncating content.
|
|
82
|
+
`fallback_reason` explains the transition. These limits also apply to `:full`.
|
|
83
|
+
|
|
84
|
+
## Development and verification
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
bundle install
|
|
88
|
+
bundle exec rake test
|
|
89
|
+
bundle exec ruby script/compatibility --write
|
|
90
|
+
BUDGET=1 bundle exec ruby --yjit bench/highlighting.rb
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The matrix enumerates every Rouge lexer, uses three source variants based on
|
|
94
|
+
Rouge's bundled MIT-licensed demos, applies 50 deterministic insert/delete/replace
|
|
95
|
+
mutations to each, and compares all per-line tokens against a fresh full lex.
|
|
96
|
+
Three extra distant-closure probes catch backward reclassification. The test suite
|
|
97
|
+
reruns the complete matrix. `MUTATIONS=5 ruby script/compatibility python rust`
|
|
98
|
+
is a short diagnostic run; `DETAILS=1` prints the full JSON failure details.
|
|
99
|
+
Only full runs with 50 or more mutations may replace the bundled matrix.
|
|
100
|
+
|
|
101
|
+
Scheduled CI updates Rouge, regenerates the matrix, reruns tests, and uploads the
|
|
102
|
+
generated source/table for review. It does not silently publish a gem or modify a
|
|
103
|
+
release. Regular CI tests Ruby 3.1–4.0 on Linux, macOS and Windows.
|
|
104
|
+
|
|
105
|
+
On the development Mac with Ruby 4.0.0/YJIT, the 5,000-line Python benchmark
|
|
106
|
+
measured 10.771 ms for the first 60 lines and 1.325 ms median for a one-line edit
|
|
107
|
+
including source materialization and convergence. One line was rescanned, with
|
|
108
|
+
80 checkpoints and 10,560 normalized payload bytes. Run the benchmark for current
|
|
109
|
+
timings and retained-object memory; budgets are 15 ms / 5 ms / 2 MiB.
|
|
110
|
+
|
|
111
|
+
Released under the [MIT license](LICENSE.txt).
|