class ElasticTabstops::Formatter

Public Class Methods

new(output_stream, tabchar: "\t", padchar: ' ') click to toggle source
# File lib/elastic_tabstops/formatter.rb, line 4
def initialize(output_stream, tabchar: "\t", padchar: ' ')
  @outstream = output_stream
  @lines = []
  @ctrls = []
  @tabchar = tabchar
  @padchar = padchar
  @split_re = /(?<=#{Regexp.escape(tabchar)})/
end

Public Instance Methods

line(text) click to toggle source

PRECONDITION: text == '' || /A.*r?nz/ === text In the regex above, `A` matches beginning-of-string, and `z` matches end-of-string. The absence of a multiline modifier (as in /abc/m) means `.` does NOT match newline.

So, in English: `text` is a string, and it is either the empty string, or it is a sequence of non-newline characters terminated by a newline.

# File lib/elastic_tabstops/formatter.rb, line 20
def line(text)
  texts = text.split(@split_re)

  # Ensure that a non-tab-terminated text exists at the end of texts
  texts << '' if texts.empty? || texts[-1][-1] == @tabchar

  emit_line_of_cells(texts)
end

Private Instance Methods

emit_line_of_cells(texts) click to toggle source

PRECONDITION: texts === Array && texts.size > 0 && texts[-1] != @tabchar

# File lib/elastic_tabstops/formatter.rb, line 32
def emit_line_of_cells(texts)
  # Remove the non-tab-terminated cell from the end.
  last = texts.pop

  # Make sure `@ctrls` covers all columns in this input line.
  @ctrls.push({ width: 0 }) while @ctrls.size < texts.size

  # Convert the array of strings into an array of cell hashes
  cells = texts.map.with_index do |text, i|
    text = text[0..-2] # remove trailing tab
    ctrl = @ctrls[i]
    ctrl[:width] = text.size if text.size > ctrl[:width]
    { text: text, ctrl: ctrl }
  end

  # Remove ctrl cells for columns that don't appear in this line.
  # Such columns had data in the previous output line, but not this one.
  @ctrls.pop while @ctrls.size > cells.size

  # Restore the special `last` cell, and store the cells in @lines.
  cells << { text: last } # NOTE: no :ctrl field
  @lines.push(cells)

  # If cells.size == 1, it's only the special `last` cell; there are no
  # formattable columns in this line. That means all the pending output
  # columns have terminated, and we finally know the :width's are final.
  write_formatted_text_to_output if cells.size == 1
end
write_formatted_text_to_output() click to toggle source
# File lib/elastic_tabstops/formatter.rb, line 61
def write_formatted_text_to_output
  @lines.each do |line|
    line.each do |cell|
      s = cell[:text]
      @outstream.write(s)
      if cell.key?(:ctrl)
        @outstream.write(@padchar * (cell[:ctrl][:width] + 1 - s.size))
      end
    end
  end
  @lines = []
end