Module: Canopus::Workspace::View::TerminalPresentable
- Included in:
- Canopus::Workspace::View
- Defined in:
- lib/canopus/workspace/view/terminal_presentable.rb,
sig/controller.rbs
Defined Under Namespace
Classes: Link
Constant Summary collapse
- ANSI_COLORS =
%w[#1b1d23 #e06c75 #98c379 #e5c07b #61afef #c678dd #56b6c2 #abb2bf #5c6370 #ff7a85 #b5e890 #ffdb8a #82c7ff #e79aff #79dce7 #ffffff].freeze
Instance Attribute Summary collapse
-
#terminal_bounds ⇒ Zaniah::Bounds?
readonly
Returns the value of attribute terminal_bounds.
-
#terminal_selection ⇒ [[Integer, Integer], [Integer, Integer]]?
readonly
Returns the value of attribute terminal_selection.
Instance Method Summary collapse
- #focus_terminal(terminal) ⇒ Tarazed::PTY?
- #terminal_color(value, fallback) ⇒ String?
- #terminal_command(direction) ⇒ Boolean
- #terminal_command_jump(command) ⇒ Boolean
- #terminal_link_at(point) ⇒ Link?
-
#terminal_open_link(point, modifiers:) ⇒ Boolean
This is intentionally an explicit user gesture, never an output handler.
- #terminal_point(point) ⇒ [Integer, Integer]?
- #terminal_scroll(delta) ⇒ nil
- #terminal_select(point, extend: false) ⇒ [[Integer, Integer], [Integer, Integer]]?
- #terminal_selected_text ⇒ String
- #terminal_toggle_command(command = nil) ⇒ Boolean
Instance Attribute Details
#terminal_bounds ⇒ Zaniah::Bounds? (readonly)
Returns the value of attribute terminal_bounds.
8 9 10 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 8 def terminal_bounds @terminal_bounds end |
#terminal_selection ⇒ [[Integer, Integer], [Integer, Integer]]? (readonly)
Returns the value of attribute terminal_selection.
8 9 10 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 8 def terminal_selection @terminal_selection end |
Instance Method Details
#focus_terminal(terminal) ⇒ Tarazed::PTY?
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 152 def focus_terminal(terminal) return terminal if @terminal_owner.equal?(terminal) store_terminal_state if @terminal_owner @terminal_owner = terminal state = terminal&.instance_variable_get(:@canopus_terminal_view_state) @terminal_scroll = state&.fetch(:scroll, 0) || 0 @terminal_selection = state&.[](:selection) @terminal_bounds = state&.[](:bounds) @terminal_visible_rows = state&.[](:visible_rows) @terminal_first = state&.fetch(:first, 0) || 0 @terminal_first_display = state&.fetch(:first_display, 0) || 0 @terminal_cell_width = state&.[](:cell_width) @terminal_line_height = state&.[](:line_height) @terminal_font_size = state&.[](:font_size) terminal end |
#terminal_color(value, fallback) ⇒ String?
58 59 60 61 62 63 64 65 66 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 58 def terminal_color(value, fallback) return @theme[fallback] if value.nil? return format("#%02x%02x%02x", *value) if value.is_a?(Array) return ANSI_COLORS[value] if value < 16 return format("#%02x%02x%02x", *Array.new(3, 8 + (value - 232) * 10)) if value >= 232 index = value - 16 ramp = [0, 95, 135, 175, 215, 255] format("#%02x%02x%02x", ramp[index / 36], ramp[index / 6 % 6], ramp[index % 6]) end |
#terminal_command(direction) ⇒ Boolean
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 103 def terminal_command(direction) terminal = displayed_terminal return false unless terminal&.respond_to?(:commands) && !terminal.grid.alternate? map = terminal_row_map(terminal) maximum = [map[:visible] - terminal.grid.rows, 0].max first_display = maximum - (@terminal_scroll || 0).floor current = map[:base] + terminal_source_for_display(map, first_display) commands = terminal.commands.select { |command| command.prompt_row >= map[:base] } command = if direction == :previous commands.reverse.find { |item| @terminal_scroll.to_i.zero? ? item.prompt_row <= current : item.prompt_row < current } elsif direction == :next commands.find { |item| item.prompt_row > current } else raise ArgumentError, "invalid terminal command direction" end terminal_command_jump(command) end |
#terminal_command_jump(command) ⇒ Boolean
121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 121 def terminal_command_jump(command) return false unless command && displayed_terminal && !displayed_terminal.grid.alternate? map = terminal_row_map source = command.prompt_row - map[:base] display = terminal_display_for_source(map, source) return false unless display maximum = [map[:visible] - displayed_terminal.grid.rows, 0].max @terminal_scroll = maximum - [display, maximum].min @terminal_selection = nil true end |
#terminal_link_at(point) ⇒ Link?
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 11 def terminal_link_at(point) return unless @terminal_bounds&.contains?(point) && displayed_terminal position = terminal_source_point(point) return unless position column, row = position cells = terminal_row(row) column -= 1 while column.positive? && cells[column].width.zero? cell = cells[column] return terminal_link(cell.hyperlink, explicit: true) if cell.hyperlink # ponytail: bare-link detection stays within one screen row; OSC 8 links # retain their metadata across wrapping and do not have this limit. text = cells.map(&:text).join offset = cells.first(column).sum { |value| value.text.bytesize } text.to_enum(:scan, /[^\s<>"'`]+/).each do match = Regexp.last_match first = text[0...match.begin(0)].bytesize next unless offset >= first && offset < first + match[0].bytesize value = match[0].sub(/[.,;]+\z/, "") value = value.delete_prefix("(").delete_suffix(")") if value.start_with?("(") value = value.delete_suffix(")") if value.count(")") > value.count("(") return terminal_link(value) end nil end |
#terminal_open_link(point, modifiers:) ⇒ Boolean
This is intentionally an explicit user gesture, never an output handler.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 37 def terminal_open_link(point, modifiers:) return false if (modifiers & %w[cmd ctrl]).empty? link = terminal_link_at(point) return false unless link if link.kind == :url return false unless @workspace.window&.respond_to?(:open_url) @workspace.window.open_url(link.target) else editor = @workspace.open(link.target) if link.line row = (link.line - 1).clamp(0, editor.buffer.line_count - 1) value = editor.buffer.line(row) prefix = value.each_char.take((link.column.to_i - 1).clamp(0, value.length)).join editor.select(editor.buffer.rope.line_start(row) + prefix.bytesize) editor.reveal_cursor end @workspace.window&.request_frame end true end |
#terminal_point(point) ⇒ [Integer, Integer]?
67 68 69 70 71 72 73 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 67 def terminal_point(point) grid = displayed_terminal.grid position = terminal_source_point(point) return unless position column, row = position [column, (row - grid.scrollback.length).clamp(0, grid.rows - 1)] end |
#terminal_scroll(delta) ⇒ nil
74 75 76 77 78 79 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 74 def terminal_scroll(delta) map = terminal_row_map maximum = [map[:visible] - displayed_terminal.grid.rows, 0].max @terminal_scroll = ((@terminal_scroll || 0) - delta).clamp(0, maximum) @terminal_selection = nil end |
#terminal_select(point, extend: false) ⇒ [[Integer, Integer], [Integer, Integer]]?
80 81 82 83 84 85 86 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 80 def terminal_select(point, extend: false) position = terminal_source_point(point) return unless position column, row = position index = [row, column] @terminal_selection = extend && @terminal_selection ? [@terminal_selection.first, index] : [index, index] end |
#terminal_selected_text ⇒ String
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 87 def terminal_selected_text return "" unless @terminal_selection first, last = @terminal_selection.sort map = terminal_row_map from_row = terminal_display_for_source(map, first.first) to_row = terminal_display_for_source(map, last.first) return "" unless from_row && to_row (from_row..to_row).map do |display_row| row = terminal_source_for_display(map, display_row) cells = terminal_row(row) from = row == first.first ? first.last : 0 to = row == last.first ? last.last : cells.length - 1 cells[from..to].to_a.map(&:text).join.rstrip end.join("\n") end |
#terminal_toggle_command(command = nil) ⇒ Boolean
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/canopus/workspace/view/terminal_presentable.rb', line 133 def terminal_toggle_command(command = nil) terminal = displayed_terminal return false unless terminal&.respond_to?(:commands) && !terminal.grid.alternate? map = terminal_row_map(terminal) unless command maximum = [map[:visible] - terminal.grid.rows, 0].max first = maximum - (@terminal_scroll || 0).floor last = [first + terminal.grid.rows - 1, map[:visible] - 1].min current = map[:base] + terminal_source_for_display(map, last) command = terminal.commands.reverse.find { |item| item.prompt_row <= current && terminal_fold_interval(item, map) } end return false unless command && terminal_fold_interval(command, map) folds = terminal_folds(terminal) folds[command.id] ? folds.delete(command.id) : folds[command.id] = true @terminal_selection = nil terminal_command_jump(command) true end |