Class: Tempest::REPL::Screen

Inherits:
Object
  • Object
show all
Defined in:
lib/tempest/repl/screen.rb

Overview

Implements the earthquake-style split layout: the bottom row holds the tempest> prompt, while the rest of the terminal scrolls timeline lines in from below. Built on the DECSTBM (top/bottom margin) escape sequence so we don’t need a full curses screen.

Sequences used:

CSI top;bottom r  set scrolling region
CSI r             reset scrolling region (full screen)
CSI row;col H     move cursor
ESC 7 / ESC 8     save/restore cursor (DECSC/DECRC)

Instance Method Summary collapse

Constructor Details

#initialize(io:, rows: nil, cols: nil) ⇒ Screen

Returns a new instance of Screen.



17
18
19
20
21
22
23
# File 'lib/tempest/repl/screen.rb', line 17

def initialize(io:, rows: nil, cols: nil)
  @io = io
  @rows = rows
  @cols = cols
  @enabled = false
  @mutex = Mutex.new
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, **kwargs, &block) ⇒ Object



88
89
90
# File 'lib/tempest/repl/screen.rb', line 88

def method_missing(name, *args, **kwargs, &block)
  @io.send(name, *args, **kwargs, &block)
end

Instance Method Details

#disableObject



38
39
40
41
42
43
# File 'lib/tempest/repl/screen.rb', line 38

def disable
  return unless @enabled
  @io.print "\e[r"
  @io.flush if @io.respond_to?(:flush)
  @enabled = false
end

#enableObject



25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/tempest/repl/screen.rb', line 25

def enable
  return unless @io.respond_to?(:tty?) && @io.tty?
  rows = @rows || detect_rows
  return unless rows && rows >= 4

  @rows = rows
  @cols ||= detect_cols
  @io.print "\e[1;#{rows - 1}r"   # scrolling region: rows 1..rows-1
  @io.print "\e[#{rows};1H"        # park cursor on the final row (prompt)
  @io.flush if @io.respond_to?(:flush)
  @enabled = true
end

#enabled?Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/tempest/repl/screen.rb', line 45

def enabled?
  @enabled
end

#flushObject



76
77
78
# File 'lib/tempest/repl/screen.rb', line 76

def flush
  @io.flush if @io.respond_to?(:flush)
end


68
69
70
# File 'lib/tempest/repl/screen.rb', line 68

def print(*args)
  @io.print(*args)
end

#puts(*lines) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/tempest/repl/screen.rb', line 49

def puts(*lines)
  @mutex.synchronize do
    if @enabled
      flat = lines.empty? ? [""] : lines.flat_map { |l| l.to_s.split("\n") }
      flat.each { |line| insert_above_prompt(line) }
    else
      # Best-effort write that doesn't shred the prompt when we don't have
      # a scrolling region in place. Reline rerender is invoked by
      # AsyncOutput; Screen itself stays neutral here.
      (lines.empty? ? [""] : lines).each do |line|
        @io.print "\r\e[2K"
        @io.puts line
      end
      @io.flush if @io.respond_to?(:flush)
    end
  end
  rerender_prompt
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/tempest/repl/screen.rb', line 84

def respond_to_missing?(name, include_private = false)
  @io.respond_to?(name, include_private)
end

#tty?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/tempest/repl/screen.rb', line 80

def tty?
  @io.respond_to?(:tty?) ? @io.tty? : false
end

#write(*args) ⇒ Object



72
73
74
# File 'lib/tempest/repl/screen.rb', line 72

def write(*args)
  @io.write(*args)
end