rsyntaxtree 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ccd433053f296c9189c9eceb87f61c91ed815ff6b1be77f1cd2115a73eaf1cd
4
- data.tar.gz: f769d68efb73c1aafc9dc9af9f0e4e7cbd7a0c741caf253905ebe11dc3c454be
3
+ metadata.gz: 231de78b2e34e37f4a5bbccd641839e8cf5ae1babc2d8d4362d86dcf782d28b4
4
+ data.tar.gz: a4d04e71e6587592540400aa9a7e23d9dadae0d19912078d5d6cc5ebd65c117d
5
5
  SHA512:
6
- metadata.gz: 0e6218bc0ddf1f4c49e8fac2c8972cbd3e0eb251fe7087150ea73f63b054fe546483588e582a0f01e52ab26c20fd883ab2c67da07adf1f3120755447a7956945
7
- data.tar.gz: b0810c0df9ede2cd1c36100ae9a3c055ece2d59261f8278ef8de8b79e07921702c7b8db83fa43f81810184c363b860bae3d9a9c1f45b4a02307f5f8220354fa8
6
+ metadata.gz: f97797690b6d8e0ad8404c00a10c061c89d2213e582915a33fc9d739305d5fb10cdd0afde6bffe0b16f504b5d5854d7dcb0249f50a22b10599638fb22180aed3
7
+ data.tar.gz: 7f5814cc9bf0875b21982cf6c143397d467fb900a7973c8f6213934387ba4f80e0605ddccec63f02bc7bacc6d182a9da1bda3237f909414ba4c64f63a1da1d12
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.1.0] - 2026-08-29
4
+
5
+ ### Changed
6
+ - The JSON output's format name is `json`: `-f json`, writing `syntree.json`.
7
+ The old name `lsif` collided with an established code-intelligence format
8
+ of the same initials, and called itself an interchange format when only
9
+ one tool had ever written or read it. It remains an accepted alias — the
10
+ CLI says so on stderr — until 3.0. The document now identifies itself in a
11
+ top-level `format` object (`name: "rsyntaxtree-json"`, schema 0.4.0) in
12
+ place of the `lsif` key.
13
+
14
+ ### Added
15
+ - A feature matrix in the JSON output carries its rows: cells split on the
16
+ tab stops, rules as rows of their own, and a matrix inside a cell
17
+ recursing. It used to arrive as an empty segment marked "matrix", its
18
+ attribute-value pairs recoverable only by re-parsing the raw label.
19
+
3
20
  ## [2.0.0] - 2026-08-28
4
21
 
5
22
  The removals announced in 1.10.0 and 1.12.0, and the fixes that came out of
data/README.md CHANGED
@@ -69,7 +69,7 @@ Connect nodes with lines or arrows:
69
69
 
70
70
  ### Multiple Output Formats
71
71
 
72
- Generate trees in PNG, SVG, PDF, TikZ, or LSIF (JSON) format.
72
+ Generate trees in PNG, SVG, PDF, TikZ, or JSON format — the last carrying the laid-out tree as structured data.
73
73
 
74
74
  ## Web Interface
75
75
 
@@ -170,7 +170,7 @@ Usage:
170
170
  | Option | Description | Default |
171
171
  |--------|-------------|---------|
172
172
  | `-o, --outdir` | Output directory | `./` |
173
- | `-f, --format` | Output format: png, svg, pdf, lsif, tikz | `png` |
173
+ | `-f, --format` | Output format: png, svg, pdf, json, tikz | `png` |
174
174
  | `-l, --leafstyle` | Leaf style: auto, triangle, bar, nothing | `auto` |
175
175
  | `-n, --fontstyle` | Font style: sans, serif, cjk, mono | `sans` |
176
176
  | `-s, --fontsize` | Font size: 6-26 | `16` |
data/bin/rsyntaxtree CHANGED
@@ -9,7 +9,7 @@ require_relative '../lib/rsyntaxtree'
9
9
  require_relative '../lib/rsyntaxtree/utils'
10
10
  require_relative '../lib/rsyntaxtree/format_converter'
11
11
 
12
- # "png, pdf, svg, lsif, or tikz" — built once from the library's
12
+ # "png, pdf, svg, json, or tikz" — built once from the library's
13
13
  # own list so the three places that name the formats cannot drift apart.
14
14
  FORMAT_LIST_MESSAGE = FORMATS.join(", ").sub(/, ([^,]+)\z/, ', or \1')
15
15
 
@@ -17,7 +17,7 @@ FORMAT_LIST_MESSAGE = FORMATS.join(", ").sub(/, ([^,]+)\z/, ', or \1')
17
17
  CONFIG_VALIDATORS = {
18
18
  outdir: ->(v) { FileTest.directory?(v) ? nil : "must be an existing directory path" },
19
19
  outfilename: ->(v) { v.is_a?(String) ? nil : "must be a string" },
20
- format: ->(v) { FORMATS.include?(v.to_s) ? nil : "must be #{FORMAT_LIST_MESSAGE}" },
20
+ format: ->(v) { FORMATS.include?(v.to_s) || v.to_s == "lsif" ? nil : "must be #{FORMAT_LIST_MESSAGE}" },
21
21
  leafstyle: ->(v) { OPTION_VALUES[:leafstyle].include?(v.to_s) ? nil : "must be auto, triangle, bar, or nothing" },
22
22
  fontstyle: ->(v) { OPTION_VALUES[:fontstyle].include?(v.to_s) ? nil : "must be sans, serif, cjk, or mono" },
23
23
  fontsize: ->(v) { v.is_a?(Integer) && v >= 6 && v <= 26 ? nil : "must be in the range of 6-26" },
@@ -133,6 +133,13 @@ opts = Optimist.options do
133
133
  opt :help, "This is a custom help message", short: :h
134
134
  end
135
135
 
136
+ # The name the JSON format wore from 1.11.0 to 2.0.0. Accepted until 3.0,
137
+ # and said out loud, the way JPG and GIF were seen out.
138
+ if opts[:format] == "lsif"
139
+ warn "Warning: the format name 'lsif' is now 'json'. The old name will be removed in RSyntaxTree 3.0."
140
+ opts[:format] = "json"
141
+ end
142
+
136
143
  Optimist.die :outdir, "must be an exsting directory path" unless FileTest.directory?(opts[:outdir])
137
144
  Optimist.die :direction, "must be one of: #{OPTION_VALUES[:direction].join(', ')}" unless OPTION_VALUES[:direction].include?(opts[:direction])
138
145
  Optimist.die :derivation, "must be one of: #{OPTION_VALUES[:derivation].join(', ')}" unless OPTION_VALUES[:derivation].include?(opts[:derivation])
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #==========================
4
- # lsif_graph.rb
4
+ # json_graph.rb
5
5
  #==========================
6
6
  #
7
7
  # Generates LSIF (Linguistic Structure Interchange Format) JSON output.
@@ -12,7 +12,7 @@ require_relative 'base_graph'
12
12
  require_relative 'utils'
13
13
 
14
14
  module RSyntaxTree
15
- class LsifGraph < BaseGraph
15
+ class JSONGraph < BaseGraph
16
16
  attr_accessor :width, :height
17
17
 
18
18
  def initialize(element_list, params, global)
@@ -30,7 +30,7 @@ module RSyntaxTree
30
30
  @global = global
31
31
  end
32
32
 
33
- def lsif_data
33
+ def json_data
34
34
  metrics = parse_list
35
35
  @width = metrics[:width] + @global[:h_gap_between_nodes] * 2
36
36
  @height = metrics[:height] + @global[:height_connector_to_text] / 2
@@ -241,14 +241,7 @@ module RSyntaxTree
241
241
  element.content.each do |l|
242
242
  next unless l[:type] == :text
243
243
 
244
- segments = l[:elements].map do |e|
245
- next if e[:decoration].include?(:whitespace) && e[:text].strip.empty?
246
-
247
- {
248
- text: e[:text].gsub(WHITESPACE_BLOCK, " ").gsub('&#62;', '>').gsub('&#60;', '<'),
249
- decorations: e[:decoration].map(&:to_s).reject { |d| d == "whitespace" }
250
- }
251
- end.compact
244
+ segments = l[:elements].map { |e| build_segment(e) }.compact
252
245
  lines << { segments: segments } unless segments.empty?
253
246
  end
254
247
 
@@ -258,6 +251,43 @@ module RSyntaxTree
258
251
  }
259
252
  end
260
253
 
254
+ def build_segment(run)
255
+ return nil if run[:decoration].include?(:whitespace) && run[:text].strip.empty?
256
+
257
+ segment = {
258
+ text: run[:text].gsub(WHITESPACE_BLOCK, " ").gsub('&#62;', '>').gsub('&#60;', '<'),
259
+ decorations: run[:decoration].map(&:to_s).reject { |d| d == "whitespace" }
260
+ }
261
+ # A feature matrix used to reach this document as an empty segment
262
+ # marked "matrix", its attribute-value pairs recoverable only by
263
+ # re-parsing the raw label — which is no way to score whether a model
264
+ # wrote CASE nom where CASE nom was wanted. The rows come through now:
265
+ # cells split on the tab stops, rules kept as rows of their own, a
266
+ # matrix inside a cell recursing.
267
+ segment[:matrix] = { rows: build_matrix_rows(run[:matrix]) } if run[:decoration].include?(:matrix) && run[:matrix]
268
+ segment
269
+ end
270
+
271
+ def build_matrix_rows(matrix_lines)
272
+ matrix_lines.filter_map do |line|
273
+ case line[:type]
274
+ when :border then { rule: "single" }
275
+ when :bborder then { rule: "double" }
276
+ when :text
277
+ cells = [[]]
278
+ line[:elements].each do |run|
279
+ if run[:decoration].include?(:tabstop)
280
+ cells << []
281
+ else
282
+ segment = build_segment(run)
283
+ cells.last << segment if segment
284
+ end
285
+ end
286
+ { cells: cells }
287
+ end
288
+ end
289
+ end
290
+
261
291
  def map_enclosure(enclosure)
262
292
  case enclosure
263
293
  when :brackets then "brackets"
@@ -273,8 +303,14 @@ module RSyntaxTree
273
303
 
274
304
  def build_json
275
305
  data = {
276
- lsif: {
277
- version: "0.3.0",
306
+ # What this document is, said by a field rather than by the key, so a
307
+ # reader checks ["format"]["name"] and the key never has to change
308
+ # again. Documents up to schema 0.3.0 said it with a top-level "lsif"
309
+ # key instead — a name that collided with the code-intelligence
310
+ # format of the same initials.
311
+ format: {
312
+ name: "rsyntaxtree-json",
313
+ version: "0.4.0",
278
314
  generator: "rsyntaxtree #{RSyntaxTree::VERSION}",
279
315
  level: "rendered"
280
316
  },
@@ -87,7 +87,7 @@ Options (the command line spells them --like-this):
87
87
 
88
88
  | option | what it decides |
89
89
  |------------|-----------------|
90
- | format | png, svg, pdf, tikz, lsif |
90
+ | format | png, svg, pdf, tikz, json (the tree as structured data) |
91
91
  | fontstyle | sans, serif, mono, cjk |
92
92
  | fontsize | 6–26 |
93
93
  | color | modern, traditional, off, gray |
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSyntaxTree
4
- VERSION = "2.0.0"
4
+ VERSION = "2.1.0"
5
5
  end
data/lib/rsyntaxtree.rb CHANGED
@@ -34,12 +34,12 @@ WHITESPACE_BLOCK = "■"
34
34
  # Every format the library can produce, and the file extension each one
35
35
  # writes. The CLI builds its --format validation, help text, and output
36
36
  # filename from these, so a new format is added here and nowhere else.
37
- FORMATS = %w[png pdf svg lsif tikz].freeze
37
+ FORMATS = %w[png pdf svg json tikz].freeze
38
38
  FORMAT_EXTENSIONS = {
39
39
  "png" => "png",
40
40
  "pdf" => "pdf",
41
41
  "svg" => "svg",
42
- "lsif" => "lsif.json",
42
+ "json" => "json",
43
43
  "tikz" => "tex" # forest code, to be included in a LaTeX document
44
44
  }.freeze
45
45
 
@@ -51,7 +51,10 @@ FORMAT_EXTENSIONS = {
51
51
  # purpose (see switched_on?); unknown keys are not rejected, because callers
52
52
  # pass their own extra parameters through.
53
53
  OPTION_VALUES = {
54
- format: FORMATS,
54
+ # lsif is the name the JSON format wore from 1.11.0 to 2.0.0, accepted as
55
+ # an alias until 3.0. It collided with a code-intelligence format of the
56
+ # same initials, and claimed an interchange nobody else had implemented.
57
+ format: FORMATS + ["lsif"],
55
58
  leafstyle: %w[auto triangle bar nothing],
56
59
  fontstyle: ["sans", "serif", "cjk", "mono", "noto-sans", "noto-serif", "noto-sans-mono", "cjk zenhei"],
57
60
  color: %w[modern traditional gray grey off none on true false],
@@ -152,7 +155,7 @@ require_relative 'rsyntaxtree/utils'
152
155
  require_relative 'rsyntaxtree/element'
153
156
  require_relative 'rsyntaxtree/elementlist'
154
157
  require_relative 'rsyntaxtree/svg_graph'
155
- require_relative 'rsyntaxtree/lsif_graph'
158
+ require_relative 'rsyntaxtree/json_graph'
156
159
  require_relative 'rsyntaxtree/tikz_generator'
157
160
  require_relative 'rsyntaxtree/version'
158
161
  require_relative 'rsyntaxtree/string_parser'
@@ -263,6 +266,8 @@ module RSyntaxTree
263
266
  else
264
267
  "off"
265
268
  end
269
+ when :format
270
+ new_params[key] = value.to_s == "lsif" ? "json" : value.to_s
266
271
  when :hyphen
267
272
  new_params[key] = value.to_s == "literal" ? "literal" : "markup"
268
273
  when :fontsize
@@ -461,7 +466,7 @@ module RSyntaxTree
461
466
  case @params[:format]
462
467
  when "png" then raster_surface_for(draw_svg, &:finish)
463
468
  when "pdf" then pdf_surface_for(draw_svg, StringIO.new, &:finish)
464
- when "lsif" then draw_lsif
469
+ when "json" then draw_json
465
470
  when "tikz" then draw_tikz
466
471
  else draw_svg
467
472
  end
@@ -535,11 +540,16 @@ module RSyntaxTree
535
540
  graph.svg_data
536
541
  end
537
542
 
538
- def draw_lsif
543
+ def draw_json
539
544
  sp = StringParser.new(@params[:data].gsub('&', '&amp;'), @params[:fontset], @params[:fontsize], @global)
540
545
  sp.parse
541
- graph = LsifGraph.new(sp.get_elementlist, @params, @global)
542
- graph.lsif_data
546
+ graph = JSONGraph.new(sp.get_elementlist, @params, @global)
547
+ graph.json_data
548
+ end
549
+
550
+ # The name the JSON output wore from 1.11.0 to 2.0.0; removed in 3.0.
551
+ def draw_lsif
552
+ draw_json
543
553
  end
544
554
 
545
555
  def draw_tikz(standalone: false, font: nil)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsyntaxtree
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoichiro Hasebe
@@ -126,7 +126,7 @@ files:
126
126
  - lib/rsyntaxtree/element.rb
127
127
  - lib/rsyntaxtree/elementlist.rb
128
128
  - lib/rsyntaxtree/format_converter.rb
129
- - lib/rsyntaxtree/lsif_graph.rb
129
+ - lib/rsyntaxtree/json_graph.rb
130
130
  - lib/rsyntaxtree/markup_parser.rb
131
131
  - lib/rsyntaxtree/notation_core.md
132
132
  - lib/rsyntaxtree/notation_examples.md