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