rsyntaxtree 2.4.1 → 2.5.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.
data/CHANGELOG.md CHANGED
@@ -1,684 +1,282 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.5.0] - 2026-11-04
4
+
5
+ ### Added
6
+ - `leaf_align` (`--leaf-align`): the leaves on one row, by lowering `words` or `subtrees` (#16, thanks @jasperandrew)
7
+
8
+ ### Fixed
9
+ - An arrowhead at the lower end of a movement path no longer sits against the corner
10
+ - A movement path no longer runs through a label that stands farther out than its two ends
11
+ - A movement path in a bottom-to-top tree runs above the words instead of through the tree
12
+ - Polyline rails no longer step where a child is a `<>` joint, or between labels drawn bottom to top
13
+ - A connector to a single child no longer leans by half a pixel
14
+
3
15
  ## [2.4.1] - 2026-09-02
4
16
 
5
17
  ### Fixed
6
- - A node shorter than its row-mates connects to its children from its own
7
- bottom. A row aligns its labels at the top, so a tall one — a feature
8
- matrix, a two-line label — reaches far below the short ones beside it. The
9
- downward connector took the row's shared bottom for its upper end, so a
10
- short node's line began at the tall neighbour's depth: a gap opened under
11
- the label, and a polyline's branch flattened into a rule above the
12
- children. No committed figure changes; the effect shows only where a row
13
- mixes label heights.
18
+ - Connectors from a short node start at its own bottom, not below a taller label in the same row
14
19
 
15
20
  ## [2.4.0] - 2026-09-02
16
21
 
17
22
  ### Added
18
- - `RSyntaxTree.escape(text, as:, hyphen:, apostrophe:)` returns notation that
19
- draws as the given text, for a program writing notation from strings it did
20
- not choose. The context (`:word`, `:phrase`, `:label`, `:cell`) decides what
21
- whitespace means; the hyphen mode has to be given because no one spelling of
22
- a hyphen serves both; `apostrophe: :keep` keeps a straight apostrophe. The
23
- test draws every result and reads the text back, so the rules are exercised
24
- rather than restated. The one text with no spelling under `hyphen: literal`
25
- — a line of nothing but hyphens, which is the rule — raises `ArgumentError`.
26
- - `\'` keeps a straight apostrophe, which the notation otherwise sets as a
27
- curly one. `\@` is an at sign.
23
+ - `RSyntaxTree.escape(text, as:, hyphen:, apostrophe:)`: notation that draws as the given text
24
+ - `\'` for a straight apostrophe and `\@` for an at sign
28
25
 
29
26
  ### Fixed
30
- - `\+` before digits is a plus sign. The path markers at the end of a label
31
- (`+1`, `+>2`) were detected without regard to a backslash, so no label could
32
- contain "+1" at all: every "C++11" and "2+2" was read as one end of a
33
- movement path.
34
- - A label containing a backslash could lose characters in the drawing. The
35
- text was placed into the SVG as a replacement string, which Ruby reads for
36
- backreferences: `\+` and `\'` were two of them.
37
- - An escaped character the tokenizer did not know (`\'`, `\@`) lost its
38
- backslash before the markup was read.
27
+ - `\+` before digits is a plus sign (labels such as "C++11" no longer start a movement path)
28
+ - Labels containing a backslash could lose characters in the drawing
29
+ - Unknown escapes (`\'`, `\@`) lost their backslash before markup was read
39
30
 
40
31
  ## [2.3.0] - 2026-09-01
41
32
 
42
33
  ### Added
43
- - `RSTError::CODES` lists every error code the library can report. A program
44
- keyed by code — one that translates the diagnosis, or tallies what kinds of
45
- mistake a writer makes — needs the whole set rather than the codes it has
46
- happened to see, and until now that set could only be recovered by reading
47
- five files and the repair table. The list only grows: a published code is
48
- not renamed or removed.
34
+ - `RSTError::CODES`: the complete list of error codes (codes are only ever added)
49
35
 
50
36
  ### Fixed
51
- - Whitespace alone is an empty input rather than a defect in the library. It
52
- reached the drawing, which walked off the end of an empty tree and reported
53
- `internal_error`, telling the caller that their own fixable input was the
54
- library's fault.
55
- - A bracketless input of a single character draws. The tokenizer counted its
56
- cursor as exhausted one character early, so `A`, and `<>` once it is one
57
- whitespace block, produced no tokens at all — though the manual has always
58
- said a label on its own draws as one leaf.
37
+ - Whitespace-only input reported as empty input instead of `internal_error`
38
+ - A single character without brackets draws as one leaf
59
39
 
60
40
  ## [2.2.0] - 2026-09-01
61
41
 
62
42
  ### Changed
63
- - Validation reports every error of a stage, not just the first one found.
64
- An input with three bad labels lists all three in the `errors` array of
65
- the JSON diagnosis, so one round of fixes covers them. The stages are
66
- ordered — options, bracket structure, labels, whole-tree checks — and a
67
- mistake stops the later stages, with a `note` saying that fixing what is
68
- listed may reveal more; nothing is reported whose appearance is an
69
- artifact of an earlier mistake. Duplicate failures collapse to one entry
70
- and the list stops at twenty. The schema is unchanged
71
- (`rsyntaxtree.error/1`): the same array, more of it.
43
+ - Validation reports every error in a stage, not only the first (up to 20; schema unchanged)
72
44
 
73
45
  ### Added
74
- - `RSGenerator.diagnose(text, options)` returns the full diagnosis as a
75
- hash — what `--validate` prints — for programmatic callers.
76
- `check_data` keeps its contract of raising the first error.
46
+ - `RSGenerator.diagnose(text, options)` returns the full diagnosis as a hash
77
47
 
78
48
  ## [2.1.0] - 2026-08-29
79
49
 
80
50
  ### Changed
81
- - The JSON output's format name is `json`: `-f json`, writing `syntree.json`.
82
- The old name `lsif` collided with an established code-intelligence format
83
- of the same initials, and called itself an interchange format when only
84
- one tool had ever written or read it. It remains an accepted alias — the
85
- CLI says so on stderr — until 3.0. The document now identifies itself in a
86
- top-level `format` object (`name: "rsyntaxtree-json"`, schema 0.4.0) in
87
- place of the `lsif` key.
51
+ - JSON output renamed from `lsif` to `json` (`-f json`, `syntree.json`); `lsif` remains an alias until 3.0
52
+ - JSON documents carry a top-level `format` object (`rsyntaxtree-json`, schema 0.4.0) in place of `lsif`
88
53
 
89
54
  ### Added
90
- - A feature matrix in the JSON output carries its rows: cells split on the
91
- tab stops, rules as rows of their own, and a matrix inside a cell
92
- recursing. It used to arrive as an empty segment marked "matrix", its
93
- attribute-value pairs recoverable only by re-parsing the raw label.
55
+ - Feature matrices in the JSON output carry their rows and cells
94
56
 
95
57
  ## [2.0.0] - 2026-08-28
96
58
 
97
- The removals announced in 1.10.0 and 1.12.0, and the fixes that came out of
98
- looking at what was left. Every figure in the gallery draws exactly as it did.
99
-
100
59
  ### Removed
101
- - JPG and GIF output. JPEG blurs line art and GIF has nothing to offer that
102
- PNG does not do better, and they were the only reason for the RMagick
103
- dependency. `-f jpg` and `-f gif` are now refused the way any unknown
104
- format is. Use `-f png`.
105
- - The RMagick dependency, and with it ImageMagick. A machine that installs
106
- this gem no longer needs ImageMagick's headers to build one, and the gem
107
- now asks for four libraries instead of five.
108
- - The deprecated aliases, which a major release is the moment to drop:
109
- `symmetrize` and its `-y` flag (say `tidy: symmetric`), `tidy_spacing`
110
- (say `hspacing`), the undocumented `tidy_nest`, and `nothing`'s
111
- undocumented spelling `none` in `leafstyle`. Each is refused by name and
112
- told what replaced it. An unknown option is passed over in silence, since
113
- a caller may hand its own parameters through, and a removed one read that
114
- way would have handed back a different figure without a word.
115
- - `symmetrize` from the LSIF `meta.source.params` block. The layout scale it
116
- duplicated, `tidy`, is written there already.
117
-
118
- ### Fixed
119
- - A triangle drawn bottom to top pointed the wrong way. The shape spans the
120
- child's text and points at the parent, and which way that is depends on
121
- which of the two sits higher — the question the connectors ask and the
122
- triangle did not. Bottom to top the figure folded through itself: the base
123
- struck through the leaf and the apex through the node. No gallery figure
124
- carries one, because every bottom-to-top example is a derivation, which
125
- draws rules instead of connectors.
126
- - Every label now keeps the same air above it as below it. The connector
127
- endpoints were measured from the layout box, whose edges sit unevenly
128
- around the ink, so every figure carried a little more space under each
129
- label than over it — visibly once a figure was tilted. The enclosures
130
- were built with symmetric clearances all along; the plain labels now get
131
- what the boxes always had. Nearly every gallery figure changes by a few
132
- pixels of line length; no label, no position and no size moves.
133
- - A movement rail could reach past the right edge of a left-to-right
134
- canvas and be cut off at it: the rail grows the canvas as it routes, and
135
- the width it grew — unlike the height — was then recomputed from the
136
- elements alone. Four pixels at the default spacing, twenty-four at
137
- hspacing 0.5.
138
- - A rule written `---` (or `===` for a double rule) was refused inside a
139
- `#( … #)` matrix, though it was drawn in every other kind of label. Two
140
- hyphens and four were accepted there and three were not — a distinction
141
- nothing could explain. The matrix grammar had no rule for it and the
142
- matrix renderer dropped every row that was not text.
143
- - The bracket of a matrix that followed something in the same cell was drawn
144
- on top of it: `AGR |1| [ … ]`, which is how a shared value is written, put
145
- the right edge of the tag and the left edge of the bracket on the same
146
- coordinate exactly. A block now keeps as much room in front of it as the
147
- bracket keeps inside it.
148
- - In a left-to-right tree, `hspacing` and `vheight` both moved the width and
149
- nothing moved the height. The layout replaces the gap that separates
150
- sisters with one of its own and was not scaling it by `hspacing`, so a
151
- left-to-right figure could be made wider but never shorter.
152
- - `storoke-width` in the style of every text element of every SVG. Renderers
153
- ignored the misspelling, so nothing ever looked wrong.
60
+ - JPG and GIF output, and with them the RMagick and ImageMagick dependency
61
+ - Deprecated aliases `symmetrize` (`-y`), `tidy_spacing`, `tidy_nest` and `leafstyle: none`
62
+ - `symmetrize` from the LSIF `meta.source.params` block
154
63
 
155
64
  ### Added
156
- - `shear`: the drawn figure tilts by the given angle (degrees, positive
157
- leaning the top to the right) and lies on a plane drawn behind it, which
158
- shows the tilt as a surface. The whole picture shears as one affine piece — layout untouched, region
159
- shades coming out parallelograms on their own, and nothing able to newly
160
- touch or cross. The plane is fill without an edge — a region shade is
161
- bounded because it marks one part of a figure off from the rest, and a
162
- line round the whole drawing would read as a frame. `shear_plane` turns
163
- it off or gives it a colour, and a transparent background takes it away
164
- for you: a clear background is asked for in order to lay the figure over
165
- something else, and a sheet under it is what would defeat that.
166
- TikZ refuses a sheared figure the way it refuses a derivation.
167
- - `vmargin`: the clearance between a label and its connectors, the same
168
- above and below, measured from the ink its row actually contains — a row
169
- of capitals pays nothing for descenders nobody wrote, and the ends of a
170
- row's connectors stay level. From 0.1 to 1.0; at nothing at all a node's
171
- box stops holding a label that draws its own shape.
172
- - Three gallery examples, in two new categories: the Indo-European family
173
- tree (Historical Linguistics), where the region shade groups branches
174
- rather than marking a c-command domain; type-driven semantic composition
175
- (Formal Semantics); and a tree of typed feature structures with structure
176
- sharing (Formal Grammar), which is the figure the matrix rule was missing.
65
+ - `shear` and `shear_plane`: tilt the figure and draw the plane it lies on
66
+ - `vmargin`: clearance between a label and its connectors (0.1–1.0)
67
+ - Gallery examples: Indo-European family tree, type-driven semantic composition, typed feature structures
177
68
 
178
69
  ### Changed
179
- - The documents name every option. Four were missing from the one-page
180
- reference and one from the README, because the test that reads the
181
- documents built its roll-call from the tables of listed and numeric
182
- values — and so never asked after the options that take on or off. It
183
- reads the roll-call from the defaults now, and the README is one of the
184
- documents it reads.
185
- - Three tests were added for the shapes of defect this release fixed: one
186
- asks every feature in the index to read the same in a label, in a matrix
187
- and in a matrix inside a matrix; one reads the drawn shapes back out of
188
- the SVG and asks whether any two of them touch; and one asks a
189
- left-to-right tree to answer to `hspacing` in its height.
190
- - The sweep draws two more figures — one with a triangle, one with a
191
- movement rail — and moves the direction, `vmargin` and `shear` among its
192
- settings, and asks of every triangle that it have an area. Each of the two
193
- defects above lived in a figure the sweep did not draw, at a direction it
194
- did not set: the settings alone could not have found either, and a
195
- triangle folded flat passes every other check there is.
196
- - The manuals say what TikZ output does with what it cannot draw: it drops
197
- it rather than refusing, so a label that is a matrix arrives with its
198
- cells run together on one line.
70
+ - A removed option is refused with its replacement named
71
+ - Every option listed in the README and the notation reference
72
+ - Manuals describe what TikZ output drops
73
+
74
+ ### Fixed
75
+ - Triangles pointed the wrong way in bottom-to-top trees
76
+ - Labels keep the same space above as below
77
+ - Movement rails cut off at the right edge of left-to-right figures
78
+ - `---` and `===` rules refused inside `#( … #)` matrices
79
+ - Matrix bracket drawn over the content before it in the same cell
80
+ - `hspacing` and `vheight` both changed the width of left-to-right trees
81
+ - Misspelled `storoke-width` in SVG text styles
199
82
 
200
83
  ### Migration
201
- - `format: "jpg"` / `"gif"` → `"png"`.
202
- - `symmetrize: "on"` → `tidy: "symmetric"`; `tidy_spacing: n` → `hspacing: n`;
203
- `leafstyle: "none"` → `"nothing"`. Each has been the documented spelling
204
- since 1.12.0.
205
- - Everything else — every other option, and every piece of notation — is
206
- unchanged.
84
+ - `format: "jpg"` / `"gif"` → `"png"`
85
+ - `symmetrize: "on"` → `tidy: "symmetric"`
86
+ - `tidy_spacing: n` → `hspacing: n`
87
+ - `leafstyle: "none"` → `"nothing"`
207
88
 
208
89
  ## [1.14.0] - 2026-08-27
209
90
 
210
91
  ### Added
211
- - The notation has a map. The reference the gem ships (`--notation`) is one
212
- page: the characters that already mean something, then every feature at a
213
- line each with the notation beside it, then the options. Both manuals open
214
- with the same list, each row pointing at the section that explains it. A test
215
- reads the feature set out of the grammar itself and draws every sample, so a
216
- feature the grammar gains with no row in the documents fails the build, and a
217
- sample that stops meaning what it says fails with it.
218
- - The changelog is on the documentation site, at /changelog, built from
219
- CHANGELOG.md and linked from the manuals and the README.
92
+ - One-page notation reference (`--notation`), with the same feature list at the top of both manuals
93
+ - Changelog page on the documentation site (/changelog)
220
94
 
221
95
  ### Changed
222
- - Every release in the changelog is dated to the day, from the git tags. Two of
223
- the old month-only dates were wrong.
224
- - What the trap section warns about is now, by definition, what the tool
225
- accepts and draws as something other than what was meant. A mistake the tool
226
- refuses is the error message's job, and its card has come down; the warning
227
- about parentheses, which the Penn Treebank conversion had made false, tells
228
- the truth again.
229
- - The arrow markers' dimensions are rounded; the defs of every SVG carried
230
- float dust in an attribute.
96
+ - Changelog dates taken from the git tags
97
+ - Pitfalls section limited to inputs that draw differently from what was meant
98
+ - Arrow-marker dimensions rounded in SVG output
231
99
 
232
100
  ### Fixed
233
- - Turning `derivation` on no longer deletes a column from a node that has no
234
- daughters. A rule name is written after a column break and names the step
235
- that produced a node from its daughters, so a node with none names no rule —
236
- but the label is read before the tree is built, and the name was taken out
237
- anyway and then had no rule to be drawn beside. `[A\tfoo]` drew "A foo" with
238
- the option off and "A" with it on. The label is put back once the tree is
239
- known.
240
- - A mistake inside a node's label keeps the name of what is wrong with it. The
241
- first raw space splits a token into a label and its children, and when the
242
- label would not parse, every cause but one was relabelled "a raw space split
243
- this" — so the message named an unknown colour while the code and the hint
244
- talked about spaces, and a caller acting on the code was sent to fix what was
245
- not wrong. Which story is right is now asked of the parser rather than
246
- guessed: the token is put back together with its spaces written as the
247
- notation writes them, and if it reads, the space was the cause.
248
- - A hex colour is three digits or six, which is what every message about colour
249
- here already said. The grammar asked for three to six, so four and five
250
- parsed, passed the validator — which does not look at a value beginning with
251
- `#` — and reached librsvg, which cannot read them and draws the label black
252
- without reporting it.
101
+ - `derivation` dropped the second column of a childless node's label
102
+ - Errors in a label split by a raw space now name the actual cause
103
+ - Hex colours accept three or six digits only
253
104
 
254
105
  ## [1.13.2] - 2026-08-27
255
106
 
256
107
  ### Fixed
257
- - A movement path is drawn as one stroke with its corners eased, in place of
258
- three lines meeting at right angles. The dash of a non-directional path now
259
- runs round each turn instead of restarting at it, and the arrowhead is the end
260
- of the stroke rather than a marker on whichever line finished last. The radius
261
- is held down to half of each run it joins, so a short leg cannot be swallowed
262
- by its own turn, and to the length of an arrowhead where there is one, so the
263
- head is never drawn onto the curve. Three gallery figures carry paths and
264
- change with it.
265
- - Digits are set in the text face again on machines that have a colour emoji
266
- font. The font chains named emoji families by name, and asking fontconfig for
267
- one is asking for the generic `emoji` family, whose preference list fontconfig
268
- then prepends to the whole pattern with a strong binding — so the emoji face
269
- arrived ahead of the Noto text faces rather than behind them. A colour emoji
270
- font carries the ASCII digits, for keycap sequences, so on an ordinary Linux
271
- desktop `[N 20]` came out in the emoji face at more than twice the width.
272
- Measurement and drawing read the same chain, so the figure held together; it
273
- was simply not the figure the same input gives elsewhere. No emoji family is
274
- named now: a codepoint no named family covers reaches fontconfig's own
275
- fallback, which is the path this was duplicating. Emoji draw as before, and
276
- the gallery is unchanged to the pixel — every raster figure is byte-identical
277
- and the SVGs differ only in the family list they carry.
108
+ - Movement paths drawn as one stroke with rounded corners
109
+ - Digits drawn in a colour emoji font on systems that have one
278
110
 
279
111
  ## [1.13.1] - 2026-08-27
280
112
 
281
113
  ### Fixed
282
- - `^` at the head of a leaf draws the triangle it asks for. The mark may be
283
- written on the node's label or on the leaf's own text — `[^NP cats]` and
284
- `[NP ^cats]` — and the documentation, the notation reference and the gallery
285
- all use both, but only the first was drawn: the second lost its caret to the
286
- parser and then a bar was drawn under it. The two now produce the same figure
287
- in every connector style, and LSIF records the edge as a triangle. Three
288
- gallery figures change accordingly, each toward what it was written to show.
289
- - An option given as an empty string is read as an option not given, rather
290
- than as a value no list contains. An HTML form posts a field for every
291
- control it carries, and a control with nothing selected posts the empty
292
- string, so a form that has outlived one of its own controls sends that field
293
- empty alongside the rest. Since option validation arrived in 1.11.0 that
294
- failed the whole request: the web UI's Download buttons answered 500 for
295
- every input, because the form still read a `format` select the page no longer
296
- had. A value that is actually wrong is still rejected.
114
+ - `^` on a leaf's own text (`[NP ^cats]`) draws a triangle, as `[^NP cats]` does
115
+ - Options given as empty strings treated as not given (fixes the web UI's Download buttons)
297
116
 
298
117
  ## [1.13.0] - 2026-08-24
299
118
 
300
119
  ### Added
301
- - Derivations. A derivation puts the words first and the result last, and
302
- joins what each step combines with one rule drawn across all of it rather
303
- than with a line to each daughter. `derivation` draws the rules and
304
- `direction: btt` turns the tree over; together they draw a CCG derivation,
305
- and `derivation` on its own marks the spans of an ordinary tree. The name of each step rides in the label after a
306
- column break and is set beside the end of its rule. A derivation runs down
307
- the page and is drawn with its rules, so `direction: ltr` and hiding the
308
- default connectors are refused rather than approximated.
309
- - `direction: btt`, the layout turned over, leaves first.
120
+ - `derivation`: one rule across each node's daughters, with rule names (CCG style with `direction: btt`)
121
+ - `direction: btt`: bottom-to-top layout
310
122
 
311
123
  ### Fixed
312
- - Connectors follow the tree when it is turned over. They kept the ends they
313
- have when the root is at the top, so every line ran from above the daughter,
314
- through its label and the mother's, to below the mother.
124
+ - Connectors in bottom-to-top layout
315
125
 
316
126
  ## [1.12.0] - 2026-08-22
317
127
 
318
- Everything needed to write this notation is now available as text, built from
319
- the files the tool itself reads, and a refusal says where that text is.
320
-
321
128
  ### Added
322
- - `--examples` prints every published example with the options it was drawn
323
- with. The gallery examples are checked against the parser that draws them,
324
- so each one is known to be accepted.
325
- - The site carries the same material as plain text for a reader that can fetch
326
- a URL but cannot run a command: `llms.txt` and `llms-full.txt`, the second
327
- holding the reference, the manual and every example in one file. Both are
328
- generated from their sources, and a test fails if they fall behind.
329
- - A refusal names where the notation is written down, once, alongside the
330
- cause and the fix. A hint repairs the mistake in front of it and says
331
- nothing about the rest, which is not enough for a caller that was guessing.
129
+ - `--examples` prints every gallery example with its options
130
+ - `llms.txt` and `llms-full.txt` on the documentation site
131
+ - Error messages point to the notation reference
332
132
 
333
133
  ### Changed
334
- - The angle bracket the reference recommends is U+27E8 and U+27E9, which it
335
- had always named while writing U+3008 and U+3009 in its own examples. The
336
- East Asian pair draws a full em wide, wider than a capital, and had spread
337
- through the gallery and into the repair that corrects ASCII brackets. Both
338
- pairs still parse; documents written before this are unaffected.
339
- - Two feature-structure figures are redrawn at the narrower bracket.
134
+ - Recommended angle brackets are U+27E8/U+27E9 (U+3008/U+3009 still parse)
135
+ - Two feature-structure figures redrawn with the narrower bracket
340
136
 
341
137
  ### Fixed
342
- - `--notation` and `--examples` answer before the input is read. Reading first
343
- meant waiting on a pipe that never closes when stdin is not a terminal,
344
- which is how a script reaches them.
138
+ - `--notation` and `--examples` no longer wait on stdin
345
139
 
346
140
  ## [1.11.0] - 2026-08-21
347
141
 
348
- Every input the tool accepts now draws, and every input it refuses says why.
349
- Line thickness follows the type size, so figures drawn at any font size keep
350
- the same balance between text and rules — existing figures come out about a
351
- fifth lighter than before.
352
-
353
142
  ### Added
354
- - `--validate` checks input without drawing or writing a file, reporting a
355
- machine-readable diagnosis on stdout and the verdict in the exit code.
356
- - `--notation` prints a short reference for the notation, which now ships
357
- with the gem.
358
- - Errors carry a code, the offending label and an offset inside it, a
359
- one-line fix, and whether rewriting the input could help. The message text
360
- is unchanged. Each cause is confirmed by applying its fix and parsing
361
- again, so a named cause is one whose fix is known to work.
362
- - A label may be nothing but a matrix: `[#(HEAD\tnoun#)]` had no spelling
363
- before, since the enclosure rule took the `#` first.
364
- - `-f tikz` on the command line, which the documentation had offered while
365
- the CLI rejected it. TikZ output also carries the line width, which it
366
- had been accepting and dropping.
367
- - A gallery figure and a heading of its own for levelling the terminals with
368
- `<>` joints.
143
+ - `--validate`: check input without drawing (JSON diagnosis on stdout, verdict in the exit code)
144
+ - `--notation`: short notation reference, shipped with the gem
145
+ - Errors carry a code, location, one-line fix and retryable flag
146
+ - A label may be a matrix alone (`[#(HEAD\tnoun#)]`)
147
+ - `-f tikz` in the CLI; TikZ output carries the line width
148
+ - Gallery figure for levelling terminals with `<>` joints
369
149
 
370
150
  ### Changed
371
- - Line width is a fraction of the font size rather than an absolute number.
372
- `1` is five per cent of the type size at any size, and the scale runs from
373
- `0.5` to `3.0` in halves where it ran `1` to `5` and meant `2` to `6`.
374
- Lines were two units wide whatever the type size, which read as heavily as
375
- a serif stem at 16 point and heavier still below that, and nothing thinner
376
- was available.
377
- - On/off options accept every spelling of off. `mirror: "no"` reversed the
378
- tree and `transparent: "0"` cut the background away, because anything but
379
- `"off"` and `"false"` was read as on.
380
- - Option values are checked against the values the option takes. A value
381
- nobody defines was taken and read as something else — `direction:
382
- "left-to-right"` laid the tree out top to bottom. The CLI has always
383
- refused these; the library did not, which left the web interface and other
384
- programmatic callers unguarded.
385
- - Colour names are checked against the CSS colour names. A name nobody
386
- defines passed validation and drew black.
387
- - Penn Treebank input converts in the library, not only in the CLI. Other
388
- callers got no conversion and no error: `(S (NP the dog))` drew as one
389
- leaf containing that text.
390
- - Font sizes go down to 6, which the web interface has always offered while
391
- the documentation said 8.
151
+ - Line width relative to font size (`0.5`–`3.0`, `1` = 5% of the font size); figures about a fifth lighter
152
+ - On/off options accept every spelling of off (`no`, `0`, …)
153
+ - Option values validated in the library, not only in the CLI
154
+ - Colour names checked against the CSS colour names
155
+ - Penn Treebank conversion in the library
156
+ - Minimum font size 6
392
157
 
393
158
  ### Fixed
394
- - Validation accepted input that drawing then refused: a movement path with
395
- one end, a tree too wide for a raster surface, a colour spec that fails
396
- only when the label is parsed. It now generates the drawing and discards
397
- it, going as far as the requested format can still refuse.
398
- - A malformed colour was reported as an unclosed enclosure, sending the
399
- writer to fix a `#` that was never the problem.
400
- - A left-to-right matrix drew its brackets on top of the attribute names.
401
- - Text in front of a tree crashed symmetrization, and an empty argument was
402
- read as a path to the current directory.
403
- - Overline was documented as missing from PNG output. It has been there.
159
+ - Validation passed input that drawing then refused
160
+ - Malformed colour reported as an unclosed enclosure
161
+ - Matrix brackets over attribute names in left-to-right trees
162
+ - Crash on text before a tree; empty argument read as a path
163
+ - Documentation said overline was missing from PNG output
404
164
 
405
165
  ## [1.10.0] - 2026-08-19
406
166
 
407
- A transitional release ahead of 2.0, which drops JPG/GIF output and the
408
- RMagick dependency.
409
-
410
167
  ### Deprecated
411
- - JPG and GIF output: the CLI prints a warning to stderr when either is
412
- requested, and the documentation marks both as deprecated. JPEG blurs
413
- line art and GIF has no use case here — use PNG. Both formats are
414
- removed in 2.0.
168
+ - JPG and GIF output (removed in 2.0)
415
169
 
416
170
  ### Changed
417
- - The library no longer loads RMagick at require time. SVG, PNG, PDF,
418
- TikZ and LSIF work without ImageMagick installed; only a JPG or GIF
419
- request needs RMagick, and a request without it reports the missing
420
- dependency as a regular input error instead of a bare `LoadError`.
171
+ - RMagick loaded only when JPG or GIF is requested
421
172
 
422
173
  ### Fixed
423
- - `RSTError` mutated the message it was given, so raising one with a plain
424
- string literal crashed inside the error class with a `FrozenError`. Every
425
- file here carries `frozen_string_literal`, which made the working form the
426
- unobvious one.
174
+ - `FrozenError` when raising `RSTError` with a string literal
427
175
 
428
176
  ## [1.9.0] - 2026-08-19
429
177
 
430
178
  ### Added
431
- - `hyphen: literal`, which trades the two readings of a hyphen: a bare one is
432
- a hyphen and `\-underlined\-` underlines. Feature names in HPSG and its
433
- relatives are full of hyphens, and escaping each one is a poor trade for a
434
- rule that work never uses.
435
- - A matrix nested in a label, written between `#(` and `#)`. The value of an
436
- attribute can be another attribute-value matrix, to any depth, which is what
437
- a feature path such as SYNSEM | LOCAL | CATEGORY | HEAD needs, and what
438
- HPSG and LFG are written in. The nested matrix draws its own brackets and
439
- lays out its own columns, and the rows after it clear its full height.
440
- - `\t` in a label cuts the line into cells. Every line is cut at the same
441
- points and each column is drawn at the width of its widest cell, so the
442
- parts line up down the label. Together with the bracket enclosure and the
443
- horizontal rule this gives attribute-value matrices — the feature structures
444
- of HPSG, LFG and the like — without spacing each row by hand, which is how
445
- the gallery's HPSG example used to do it.
446
- - `color: gray`, a scheme that keeps node and leaf labels black and draws the
447
- connectors, triangles and movement paths in grey. It is for diagrams whose
448
- links outnumber their labels — an ontology, a network of constructions —
449
- where a page of black lines buries the text. `grey` is accepted too.
450
- - Gallery examples for two more frameworks: LFG (an annotated c-structure and
451
- the f-structure it maps to, after Kaplan & Bresnan 1982 and Bresnan 2001)
452
- and DRT (a discourse representation structure, after Kamp 1981).
453
- - The example gallery scales each figure by its own width rather than by one
454
- factor for all of them, so a small tree and a wide one are read at a similar
455
- apparent size; a figure now fits its row instead of scrolling, loads lazily,
456
- and carries an anchor of its own.
179
+ - `hyphen: literal`: a bare hyphen is a hyphen and `\-…\-` underlines
180
+ - Nested matrices with `#(` … `#)`
181
+ - `\t` splits a label into aligned columns, for attribute-value matrices
182
+ - `color: gray` (or `grey`): black labels with grey connectors and paths
183
+ - Gallery examples for LFG and DRT
184
+ - Gallery figures scaled by their own width and loaded lazily, each with an anchor
457
185
 
458
186
  ### Changed
459
- - Every box and circle in a figure is drawn at one size and on one centre
460
- line. The size used to come from the line's height, which left a box
461
- standing a head taller than the numeral inside it, and each shape was
462
- centred on its own glyph, so the box around `s` sat lower than the one
463
- around `G`. A shape is now drawn at a fixed fraction of the font size,
464
- centred on a capital, and grows only for content that will not fit. Every
465
- figure with a boxed or circled label is redrawn.
466
- - The SVG's declared width and height agree with its viewBox. They did not,
467
- so every figure was scaled down by a few percent and letterboxed inside
468
- its own canvas.
187
+ - Boxes and circles drawn at one size on one centre line
188
+ - SVG width and height match the viewBox
469
189
 
470
190
  ### Fixed
471
- - TikZ export dropped everything inside a nested matrix: a feature structure
472
- came out as its outermost attribute names and nothing else. The export still
473
- cannot draw the brackets, but it keeps what they hold. The documented list of
474
- what TikZ does not carry now names column alignment, nested matrices and the
475
- grey line scheme.
476
- - LSIF records which of the two readings of a hyphen the input was parsed
477
- under. It records the input verbatim, and the same string means different
478
- things under the two, so a reader could not re-parse it.
479
- - A line-type connection with only one end raised a NoMethodError from inside
480
- the drawing instead of being reported as the input error it is.
481
- - A line-type connection between two nodes was anchored a full inter-node gap
482
- outside each of them, so the link fell short of both boxes where the gap was
483
- wide and reached inside them where it was narrow. It now runs between the
484
- boxes' own edges, a quarter of a gap short of each. The double arrowhead
485
- keeps the shape it had but is sized to the link, instead of one fixed size
486
- that spilled over both boxes on a short link. The same anchors are used in
487
- left-to-right layout, where a link between siblings had been drawn diagonally
488
- from the movement-path anchors and now runs straight between the facing
489
- edges.
490
- - Two nodes joined by a line-type connection are laid out far enough apart for
491
- a full-size arrowhead between them. Where the layout had packed them closer
492
- than the arrow is wide — the two leaves at the foot of the quicksort figure —
493
- the arrow was drawn small to fit; now the pair is spread and the arrow keeps
494
- its size. Only pairs that carry a link move, and only when they need to.
191
+ - TikZ export dropped the contents of nested matrices
192
+ - LSIF records the hyphen mode
193
+ - A one-ended line connection raised `NoMethodError`
194
+ - Line connections anchored to the facing box edges, with arrowheads sized to the link
195
+ - Nodes joined by a line connection spaced for a full-size arrowhead
495
196
 
496
197
  ## [1.8.2] - 2026-08-17
497
198
 
498
199
  ### Changed
499
- - `tidy: high` now compresses as far as its name promises. The level-balance
500
- floor added in 1.8.0, which keeps a pair of siblings from being tucked
501
- narrower than the level below it, applied to `medium` and `high` alike and
502
- bound first in nearly every tree: across the 75 gallery examples the two
503
- modes produced identical figures 70 times. The floor now applies to `medium`
504
- only, leaving `high` free to trade even branch angles for width — 29 of the
505
- 75 examples now differ, by up to 19%. `off`, `low` and `medium` are
506
- unchanged; `high`, like `symmetric` at the other end of the scale, is for
507
- figures that ask for it.
508
- - Mathematical alphanumerics (U+1D400–, such as the little *v* of *v*P) are
509
- named in the family chains. Neither Noto Sans nor Noto Serif covers the
510
- block, so the glyphs came from whatever the machine offered: Noto Sans Math
511
- on Alpine, DejaVu Serif on Debian/Ubuntu, STIX Two Math on macOS. The serif
512
- style now asks for a serif source first, so a serif tree no longer shows a
513
- sans *v*. The Docker images install `font-dejavu` for it.
200
+ - `tidy: high` compresses further than `medium`
201
+ - Mathematical alphanumerics (U+1D400–) named in the font chains; serif trees use a serif source
514
202
 
515
203
  ### Fixed
516
- - The CLI printed parse errors to stdout and exited 0, so a script generating
517
- figures in bulk could not tell a rejected input from a drawn one. Errors go
518
- to stderr and the exit status is 1.
519
- - Dropped `Noto Sans Mono SemiCondensed` from the mono chain. It is a width
520
- style of the variable Noto Sans Mono rather than a family of its own, and
521
- resolved nowhere on macOS, Debian/Ubuntu or Alpine; the chain fell through
522
- to `Noto Sans Mono`, which it now names directly.
204
+ - CLI errors go to stderr with exit status 1
205
+ - `Noto Sans Mono SemiCondensed` removed from the mono chain
523
206
 
524
207
  ## [1.8.1] - 2026-08-15
525
208
 
526
209
  ### Fixed
527
- - Arabic rendered as isolated, unjoined letterforms in some environments.
528
- Scripts outside Latin and CJK were left to the system's generic font
529
- fallback, which is not the same font on every machine: on Alpine the Arabic
530
- block was claimed by Noto Sans Math, which has the glyphs but no joining
531
- rules, while on Debian/Ubuntu the same text fell to DejaVu Sans. The family
532
- chains now name the scripts the gallery covers — Arabic (`Noto Sans Arabic`,
533
- with `Noto Naskh Arabic` for the serif style), Hebrew, Devanagari, Thai and
534
- Khmer — so machines that have those fonts installed produce the same shapes.
535
- Mathematical alphanumerics (U+1D400–) are not named yet and still vary by
536
- environment. Gallery example 067 (Arabic) was affected and has been
537
- regenerated.
538
- - Emoji were measured with one font and drawn with another where a colour
539
- emoji font was installed: Pango selects `Noto Color Emoji`, but the
540
- librsvg/Cairo pipeline does not rasterise its bitmap glyphs, so the drawing
541
- fell back to whatever outline font happened to cover the codepoint. The
542
- project's Docker images now install the monochrome Noto Emoji and leave the
543
- colour build out.
210
+ - Arabic drawn as unjoined letterforms on some systems
211
+ - Emoji measured and drawn with different fonts where a colour emoji font was installed
544
212
 
545
213
  ### Changed
546
- - The Docker images install the Noto packages for Arabic (including Naskh),
547
- Hebrew, Devanagari, Thai and Khmer, and carry a fontconfig rule that removes
548
- the Arabic ranges from Noto Sans Math while keeping its mathematical
549
- alphanumerics (used for the little *v* of *v*P).
550
- - Documentation records how to override any of the named families with a
551
- fontconfig alias, for users who prefer their own script fonts.
214
+ - Font chains name Arabic, Hebrew, Devanagari, Thai and Khmer families
215
+ - Docker images install Noto fonts for those scripts, with a fontconfig rule for Noto Sans Math
216
+ - Documentation on overriding font families with fontconfig
552
217
 
553
218
  ## [1.8.0] - 2026-08-15
554
219
 
555
220
  ### Added
556
- - `tidy` layout scale, one option covering every layout mode from the most
557
- spacious to the most dense: `symmetric` (radical symmetrization) | `off` |
558
- `low` (contour packing, strict leaf positions) | `medium` (packing with
559
- cross-row tucking that never lets two leaves swap their left-right order) |
560
- `high` (free tucking; leaf order kept per row only). Connector heights
561
- adjust automatically: a small height budget (5% of the tree's height) is
562
- spent on the levels whose branches spread widest, evening out branch
563
- angles. Tidy never produces overlapping labels (collisions roll back).
564
- - `mirror` option: flips the finished layout horizontally for the
565
- right-to-left tree convention of Arabic/Hebrew syntax (composes with
566
- `direction`).
567
- - `hspacing` option: scales every horizontal gap in every layout mode — the
568
- horizontal counterpart of `vheight`/connector height.
569
- - Pass-through empty nodes: a node labeled only `<>` renders as an invisible
570
- joint with the connector running continuously through it, so a `<>` chain
571
- aligns a shallow leaf with deeper leaves without a broken line.
572
- - Full CJK coverage in every font style: the family chains fall back to
573
- Noto Sans/Serif/Mono CJK, so Hangul and simplified/traditional Han render
574
- in all styles (the standalone Noto JP faces carry no Hangul).
575
- - Example gallery: Multilingual category (the same UD-PUD sentence in nine
576
- languages, constituency derived mechanically from the dependency
577
- annotation; the Arabic example demonstrates `mirror`), a Morphology
578
- category, and per-figure tidy settings across the whole gallery.
579
- - CI on GitHub Actions (Ruby 3.2/3.4).
221
+ - `tidy` layout scale: `symmetric` | `off` | `low` | `medium` | `high`
222
+ - `mirror` option for right-to-left trees
223
+ - `hspacing` option for horizontal gaps
224
+ - Pass-through `<>` nodes for levelling leaves
225
+ - CJK coverage in every font style
226
+ - Gallery: Multilingual and Morphology categories
227
+ - CI on GitHub Actions (Ruby 3.2/3.4)
580
228
 
581
229
  ### Changed
582
- - LSIF output records the layout settings a reader cannot recover from the
583
- coordinates: `geometry.mirror`, and `tidy`, `mirror`, `direction` and
584
- `horizontal_spacing` under `meta.source.params`.
585
- - The standalone `symmetrize` option and `-y` flag are deprecated aliases of
586
- `tidy: symmetric`; `tidy_spacing` is a deprecated alias of `hspacing`.
587
- - `--direction` now owns the `-d` short flag (it had been auto-assigned to
588
- `--hide-default-connectors`, so the documented `-d ltr` silently did
589
- nothing). Every published short flag is now declared explicitly rather
590
- than derived from the option set. One undocumented auto-assignment moved
591
- as a result: `-e` was `--direction` in 1.7.0 and is `--version` here.
592
- - The gem no longer bundles font files (38MB that were never opened at
593
- runtime since the Pango migration): fonts resolve by family name through
594
- fontconfig. See README for the system font packages. The dead `--font`
595
- CLI option (its value was never read) is gone.
230
+ - LSIF records `geometry.mirror` and the layout settings in `meta.source.params`
231
+ - `symmetrize` (`-y`) and `tidy_spacing` deprecated
232
+ - `-d` is `--direction` (it had been taken by `--hide-default-connectors`)
233
+ - Font files no longer bundled; fonts resolve through fontconfig; `--font` removed
596
234
 
597
235
  ### Fixed
598
- - Boolean options left at their defaults are no longer misread as enabled
599
- when the caller passes only a partial parameter set (e.g. `tidy: off`
600
- rendered as `symmetric` in the web UI).
236
+ - Boolean options left at their defaults misread as on with a partial parameter set
601
237
 
602
238
  ## [1.7.0] - 2026-08-13
603
239
 
604
240
  ### Changed
605
- - Text measurement now uses Pango, the same engine (and the same fontconfig
606
- font-fallback resolution) librsvg uses to render the output, instead of
607
- RMagick with the bundled font files. Labels in any script are measured with
608
- the font that actually draws them, which fixes off-center labels for scripts
609
- the bundled fonts do not cover (e.g. Khmer, #14) without bundling per-script
610
- fonts. Horizontal dimensions of the output may change slightly.
611
- - Vertical rhythm is now derived deterministically from the font size
612
- (1.4 x size, matching the previous Latin line height) and is identical
613
- across scripts; the old engine spaced Japanese text 1.5x and WenQuanYi
614
- 1.25x, so mixed-language documents had inconsistent spacing.
615
- - The font-family lists used in the SVG output and in measurement are now
616
- defined in a single place (`FONT_FAMILIES` / `FontFamily`).
241
+ - Text measured with Pango instead of RMagick (fixes off-centre Khmer labels, #14)
242
+ - Line height fixed at 1.4 × font size for every script
243
+ - Font family lists defined in one place
617
244
 
618
245
  ### Added
619
- - Runtime dependency on the `pango` gem (ruby-gnome). No new system
620
- requirements: librsvg already depends on Pango.
246
+ - Runtime dependency on the `pango` gem
621
247
 
622
248
  ## [1.6.3] - 2026-07-19
623
249
 
624
250
  ### Fixed
625
- - Packaged files no longer carry owner-only permissions. `gem build` preserves
626
- on-disk modes, so gems built from a checkout with `0600`/`0700` files shipped
627
- a library unreadable — and a `rsyntaxtree` CLI unexecutable — by anyone but
628
- the owner after `sudo gem install`. File modes are now normalized before the
629
- gem is built: `0755` for commands (`bin/`, `exe/`) and shebang scripts, `0644`
630
- for everything else. This also drops stray executable bits that the working
631
- tree had picked up on data files (images, fonts, Markdown, CSS, library
632
- sources).
251
+ - Packaged files no longer carry owner-only permissions
633
252
 
634
253
  ## [1.6.2] - 2026-06-26
635
254
 
636
255
  ### Fixed
637
- - Region shade no longer touches the image edge when its padded bounds reach
638
- past the tree's natural extent (e.g. a deep enclosed/multi-line node): the
639
- canvas now grows with a margin around the shaded plane.
256
+ - Region shade no longer touches the image edge
640
257
 
641
258
  ## [1.6.1] - 2026-06-26
642
259
 
643
260
  ### Improved
644
- - Region shade rendering wraps the subtree more cleanly: wider, balanced
645
- padding; bracket/rectangle enclosures are kept inside the plane; the incoming
646
- parent connector stops just short of the plane (no overlap or touching); and
647
- the margin is consistent between root and non-root regions. Works in both
648
- top-to-bottom and left-to-right layouts (the connector-facing edge is the top
649
- in TTB and the left in LTR, with symmetric padding on the other sides).
261
+ - Cleaner padding and connector clearance around region shades
650
262
 
651
263
  ### Added
652
- - Example 065: nested (overlapping) region shades, shown as progressively
653
- darker gray.
264
+ - Example 065: nested region shades
654
265
 
655
266
  ## [1.6.0] - 2026-06-25
656
267
 
657
268
  ### Added
658
- - Region shade (`%` prefix): paints a semi-transparent plane behind the whole
659
- subtree a node governs, for marking c-command/binding domains and cognitive
660
- grammar dominions. Color reuses the `@color:` syntax; bare `%` uses light gray.
661
- Each plane has a darker same-color border for visibility on white. An explicit
662
- shade color is always honored (consistent with `@color:` node text); use bare
663
- `%` for a gray monochrome shade. Works in both TTB and LTR layouts and across
664
- SVG/PNG/PDF/JPG/GIF.
665
- - Region shade support in TikZ export (via `forest` `fit to=tree`) and in LSIF
666
- node `style.region`. TikZ region colors (names and hex, including SVG/CSS
667
- names like `lightblue` that xcolor lacks) are emitted as explicit RGB so the
668
- output compiles.
669
- - `\%` escape for a literal leading percent sign.
670
- - Typographic apostrophe: a straight ASCII apostrophe (`'`) in a label is now
671
- rendered as a curly apostrophe (`’`, U+2019) for smarter typography, e.g. the
672
- X-bar prime in `T'`. Applied to all fonts and measured before layout so
673
- spacing stays correct.
674
- - Example 064: region shade for a c-command domain.
269
+ - Region shade (`%` prefix), coloured with `%@color:`
270
+ - Region shade in TikZ export and in LSIF (`style.region`)
271
+ - `\%` for a literal leading percent sign
272
+ - Straight apostrophes drawn as curly apostrophes (`’`)
273
+ - Example 064: region shade for a c-command domain
675
274
 
676
275
  ### Changed
677
- - LSIF output version bumped to `0.3.0` (adds node `style.region`).
276
+ - LSIF version 0.3.0
678
277
 
679
278
  ### Fixed
680
- - Region shade on the root/topmost node no longer clipped by the canvas: the
681
- SVG viewBox now grows to include region planes that extend past the tree.
279
+ - Region shade on the root clipped by the canvas
682
280
 
683
281
  ## [1.5.0] - 2026-04-06
684
282