Class: ASTTransform::Layout

Inherits:
Object
  • Object
show all
Defined in:
lib/ast_transform/layout.rb

Overview

Line-addressed output: text is placed at absolute line numbers, top to bottom, and the cursor never rewinds. When a placement's target line is already behind the cursor, the text is packed (; ) onto the current line instead — Ruby lets statements share a physical line, so alignment degrades locally and the next placement whose target is still ahead re-anchors. Knows nothing about Ruby structure or ASTs; callers decide WHAT goes on WHICH line, the layout owns the pad-or-pack mechanics.

Instance Method Summary collapse

Constructor Details

#initializeLayout

Returns a new instance of Layout.



10
11
12
13
# File 'lib/ast_transform/layout.rb', line 10

def initialize
  @lines = []
  @sealed = false
end

Instance Method Details

#cursorObject

The line number currently being written; the next fresh line would be +cursor + 1+.



16
17
18
# File 'lib/ast_transform/layout.rb', line 16

def cursor
  @lines.size
end

#pack(text) ⇒ Object

Appends text to the current line with a ; separator — unless the current line is sealed, in which case the text opens a fresh line (alignment degrades by one more line; validity is preserved). The last line is never blank here: padding blanks are only created inside place, which immediately overwrites the padded line.



53
54
55
56
57
58
59
60
# File 'lib/ast_transform/layout.rb', line 53

def pack(text)
  if @lines.empty? || @sealed
    @lines << text
  else
    @lines[-1] = "#{@lines.last}; #{text}"
  end
  @sealed = false
end

#place(target_line, text, column: nil, seal: false) ⇒ Object

Places text at target_line when the cursor hasn't passed it; otherwise packs onto the current line. Multi-line text advances the cursor by its height. When opening a fresh line, the first line is indented to column — cosmetic only (leading whitespace is never significant in emitted code), but it keeps the artifact visually close to the source. Packed text ignores the column, as do continuation lines (they keep their own relative indentation).

seal declares the text's last line unextendable (the caller's property — e.g. it terminates a heredoc, whose terminator must stand alone): a subsequent pack opens a fresh line instead of joining it.



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/ast_transform/layout.rb', line 28

def place(target_line, text, column: nil, seal: false)
  first, *rest = text.split("\n")

  if target_line && target_line > @lines.size
    @lines << '' while @lines.size < target_line
    @lines[-1] = indented(first, column)
  else
    pack(first)
  end

  @lines.concat(rest)
  @sealed = seal
end

#place_on_fresh_line(text) ⇒ Object

Appends text on a new line unconditionally — for text that must never be ;-packed after a statement (e.g. keywords).



44
45
46
47
# File 'lib/ast_transform/layout.rb', line 44

def place_on_fresh_line(text)
  @lines << text
  @sealed = false
end

#to_sourceString

Returns the laid-out text, with a trailing newline.

Returns:

  • (String)

    the laid-out text, with a trailing newline.



63
64
65
# File 'lib/ast_transform/layout.rb', line 63

def to_source
  "#{@lines.join("\n")}\n"
end