Class: Typr::Text

Inherits:
Stack show all
Defined in:
lib/text.rb

Overview

A vertically scrollable text viewer with word-wrapping, header bar, row selection via hint characters, and search.

Accepts String, IO, or StringIO input. Content is split into display rows by newlines and line width, then rendered as a sliding window.

Example

text = Typr::Text.new(
input: StringIO.new("Hello\nworld"),
header: "My Doc",
top: 2, left: 0.1, right: 0.9, bottom: -4,
border: :round,
colors: { header: [:yellow, :grey30] }
)
text.draw

Constant Summary

Constants included from Typr

COLORS, INPUT, KEY_ESCAPE, KEY_PAGEDOWN, KEY_PAGEUP, KEY_RETURN, KEY_TAB, MIMETYPES, MODES

Instance Attribute Summary collapse

Attributes inherited from Stack

#header, #hints, #hints_start, #keymap, #selected, #separator, #start

Attributes inherited from Space

#borders, #bottom, #colors, #interval, #left, #margin, #right, #top

Instance Method Summary collapse

Methods inherited from Stack

#cycle, #down, #draw_hints, #headspace, #height, #page, #page_down, #page_up, #pick, #reset, #send, #to_bottom, #to_top, #up

Methods inherited from Space

#border=, #draw_border, #height, #start, #stop, #width

Methods included from Typr

#background, clear, #coerce_type, #color, #color_code, column, exit, #fade, #foreground, #get_background, #get_foreground, height, init, #mode, #mode_code, #move, #move_code, on_resize, position, #prepare, #printables, read, #real_size, row, #show, size, width

Constructor Details

#initialize(args = {}) ⇒ Text

Creates a new Text widget.

text = Typr::Text.new(input: "hello", header: "Title")
text = Typr::Text.new(input: $stdin, tabspace: 4)


119
120
121
122
123
124
125
126
# File 'lib/text.rb', line 119

def initialize args={}
  @tabspace = 2
  @data, @lines, @tail = "", [0], ""
  super args
  @colors = { header: :yellow }.merge( @colors || {} )
  @lastwidth = width
  self << @input
end

Instance Attribute Details

#tabspaceObject

Tabstop width in spaces. Default: 2.



26
27
28
# File 'lib/text.rb', line 26

def tabspace
  @tabspace
end

Instance Method Details

#build(input = nil) ⇒ Object Also known as: <<

Rebuilds the internal line buffer from input. Splits text at newlines and word boundaries to fit the current width. Pass nil to re-parse from the existing buffer (triggered on resize).

text.build("one\ntwo three")  # => #<Typr::Text ...>
text << "appended line"       # alias for build


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/text.rb', line 35

def build input=nil
  ( @lines = [0]; input = "" ) if rebuild = !input
  return self if rebuild && input == ""
  input = StringIO.new( input.to_s.dup ) unless input.respond_to?(:read)
  @tail = ""
  until input.closed? or input.eof?
    capacity = width - @margin.size - @tail.length
    chunk = (capacity < 1) ? "x" : capacity
    line = @tail + input.read(chunk)
    if line.empty?
      break
    else
      wrap_idx = line.index("\n") || line.rindex(" ") || line.length
      wrap = [wrap_idx, line.length].min + 1
      @data += line[0...wrap]
      if wrap < line.length
        @tail = line[wrap..-1]
        @lines << @data.length
      else
        @tail = ""
      end
    end
    break if rebuild && line[-1] == "\n"
  end
  until @tail.empty?
    wrap_idx = @tail.index("\n") || @tail.rindex(" ") || @tail.length
    wrap = [wrap_idx, @tail.length].min + 1
    @data += @tail[0...wrap]
    if wrap < @tail.length
      @lines << @data.length
      @tail = @tail[wrap..-1]
    else
      @tail = ""
    end
  end
  @lines << @data.length unless @lines.last == @data.length
end

#drawObject

Full draw cycle: rebuilds on resize, computes visible lines, and renders the viewport with borders and hints.

text.draw


100
101
102
103
104
105
106
# File 'lib/text.rb', line 100

def draw
  (build; @lastwidth = width) if width != @lastwidth
  range = @lines[@start..@start+height]
  page = StringIO.new @data[range.first..range.last]
  @page=range[1..-1].map{|pos| page.read(pos-range.first-page.pos).rstrip}
  super
end

#positions(row) ⇒ Object

Returns word-boundary column offsets for a given page row. Used internally to place hint markers between words.

text.positions(2)  # => [0, 6, 10, ...]


91
92
93
# File 'lib/text.rb', line 91

def positions row
  [0] + @page[row-1].chars.map.with_index{|c,i| i+1 if c == @separator}.compact
end

Renders a single display row. Pass an Integer for a content row or :header for the header bar.

text.print(:header)  # renders the header bar
text.print(3)        # renders row 3 of the current page


80
81
82
83
84
# File 'lib/text.rb', line 80

def print row=nil
  header = ( row == :header )
  return super unless row and @lines[ row + 1 ] unless header
  show prepare( ( header ? @header : @page[ row-@start ] ), width )
end

#rowsObject

Total number of text lines in the buffer.

text.rows  # => 42


112
# File 'lib/text.rb', line 112

def rows; @lines.count end