Class: Echoes::Editor
- Inherits:
-
Object
- Object
- Echoes::Editor
- Defined in:
- lib/echoes/editor.rb
Overview
Vim-equivalent editor pane backed by ‘Rvim::Editor`. Editing, `:w` (write), `:q` (quit), search, visual mode, undo/redo and the rest of rvim’s surface all work — this class is the rendering shim that turns rvim’s editor state into the styled segments echoes’ Screen wants. The bottom row is reserved for the statusline (mode / filename / modified marker / line:col) or, when in command/search mode, for the cmdline (‘:`, `/`, or `?` prompt with the typed text and cursor).
Constant Summary collapse
- COLOR_MAP =
rvim’s syntax highlighter labels each token with a vim-style color symbol (‘:Comment`, `:String`, …). Map to ANSI palette indices that this Screen’s Cell.fg uses.
{ Comment: 8, # bright black / grey String: 2, # green Keyword: 5, # magenta Symbol: 3, # yellow Number: 3, # yellow Constant: 6, # cyan Function: 4, # blue Type: 6, # cyan Special: 3, # yellow PreProc: 5, # magenta Operator: 14, # bright cyan Identifier: 7, # white }.freeze
- DEFAULT_SEGMENT =
{ fg: nil, bg: nil, bold: false, italic: false, underline: false, inverse: false, }.freeze
- SPECIAL_KEY_MAP =
Forward a single character (or escape sequence) to the editor. Accepts Strings like ‘j’, ‘G’, “x04” (Ctrl-D), or “e[A”. Maps macOS NSEvent special-key codepoints to vim equivalents so the arrow keys “just work” in viewer mode.
{ "\u{F700}" => 'k', # Up "\u{F701}" => 'j', # Down "\u{F702}" => 'h', # Left "\u{F703}" => 'l', # Right "\u{F72C}" => "\x02", # PageUp → Ctrl-B "\u{F72D}" => "\x06", # PageDown → Ctrl-F "\u{F729}" => 'g', # Home (caller may follow with another 'g') "\u{F72B}" => 'G', # End }.freeze
Instance Attribute Summary collapse
-
#file ⇒ Object
readonly
Returns the value of attribute file.
Instance Method Summary collapse
-
#closed? ⇒ Boolean
rvim sets ‘quit` after `:q` / `:q!` / `:wq`.
-
#cursor_position ⇒ Object
(row, col) of the cursor within the viewport.
-
#display_filename ⇒ Object
Filename for the window title; appends ‘[+]` while the buffer has unsaved changes (vim convention).
- #feed_key(ch) ⇒ Object
-
#initialize(file:, rows:, cols:) ⇒ Editor
constructor
A new instance of Editor.
-
#mode_label ⇒ Object
Short label for the current vim mode (used in the statusline and exposed for window-title / future status-bar plumbing).
-
#resize(rows:, cols:) ⇒ Object
Match rvim’s window dimensions to the host pane’s.
-
#visible_segments ⇒ Object
Visible window’s lines as Arrays of styled-segment Hashes (‘fg:, bg:, bold:, italic:, underline:, inverse:`), one Array per visible row.
Constructor Details
#initialize(file:, rows:, cols:) ⇒ Editor
Returns a new instance of Editor.
40 41 42 43 44 45 46 47 48 49 |
# File 'lib/echoes/editor.rb', line 40 def initialize(file:, rows:, cols:) require 'rvim' @editor = Rvim::Editor.new(Reline.core.config) @rows = rows @cols = cols @file = file resize(rows: rows, cols: cols) @editor.open(file) if file && File.exist?(file) @lang = @file ? Rvim::Syntax.detect_language(@file) : nil end |
Instance Attribute Details
#file ⇒ Object (readonly)
Returns the value of attribute file.
38 39 40 |
# File 'lib/echoes/editor.rb', line 38 def file @file end |
Instance Method Details
#closed? ⇒ Boolean
rvim sets ‘quit` after `:q` / `:q!` / `:wq`. The host polls this via `Pane#alive?` and reaps the pane like it would a dead shell.
124 125 126 |
# File 'lib/echoes/editor.rb', line 124 def closed? @editor.quit? end |
#cursor_position ⇒ Object
(row, col) of the cursor within the viewport. In cmdline modes (:ex / :search_*) the cursor sits on the bottom row at the end of the cmdline text; otherwise it tracks the editor cursor in the buffer body.
108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/echoes/editor.rb', line 108 def cursor_position if cmdline_mode? text = cmdline_text [@rows - 1, text.length.clamp(0, @cols - 1)] else win = @editor.current_window top = win&.scroll_top || 0 row = (@editor.line_index || 0) - top col = @editor.byte_pointer || 0 body_max = [@rows - 2, 0].max [row.clamp(0, body_max), col.clamp(0, @cols - 1)] end end |
#display_filename ⇒ Object
Filename for the window title; appends ‘[+]` while the buffer has unsaved changes (vim convention). Returns `’[No Name]‘` for buffers opened with no path (e.g. `:enew`).
140 141 142 143 |
# File 'lib/echoes/editor.rb', line 140 def display_filename base = @file && !@file.empty? ? File.basename(@file) : '[No Name]' @editor.modified ? "#{base} [+]" : base end |
#feed_key(ch) ⇒ Object
77 78 79 80 81 82 83 |
# File 'lib/echoes/editor.rb', line 77 def feed_key(ch) mapped = SPECIAL_KEY_MAP[ch] || ch mapped.each_char { |c| @editor.send(:dispatch_synthesized_key, c) } true rescue false end |
#mode_label ⇒ Object
Short label for the current vim mode (used in the statusline and exposed for window-title / future status-bar plumbing).
130 131 132 133 134 135 |
# File 'lib/echoes/editor.rb', line 130 def mode_label return :cmdline if cmdline_mode? return :visual if @editor.visual_mode return :insert if @editor.send(:editing_mode_label) == :vi_insert :normal end |
#resize(rows:, cols:) ⇒ Object
Match rvim’s window dimensions to the host pane’s. Called on construction and on later ‘Pane#resize`.
53 54 55 56 57 58 59 60 |
# File 'lib/echoes/editor.rb', line 53 def resize(rows:, cols:) @rows = rows @cols = cols win = @editor.current_window return unless win win.height = rows win.width = cols end |
#visible_segments ⇒ Object
Visible window’s lines as Arrays of styled-segment Hashes (‘fg:, bg:, bold:, italic:, underline:, inverse:`), one Array per visible row. The last row is the statusline (or cmdline, in `:`/`/`/`?` modes); the rows above are buffer text padded with vim-style `~` markers when shorter than the pane.
90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/echoes/editor.rb', line 90 def visible_segments win = @editor.current_window lines = @editor.current_buffer&.lines || [] top = win&.scroll_top || 0 body_rows = [@rows - 1, 1].max # reserve last row for status/cmdline slice = lines[top, body_rows] || [] out = slice.map { |line| line_to_segments(line) } while out.size < body_rows out << [DEFAULT_SEGMENT.merge(text: '~', fg: 4)] end out << bottom_row_segments out end |