Class: RuVim::Editor

Inherits:
Object
  • Object
show all
Defined in:
lib/ruvim/editor.rb

Constant Summary collapse

OPTION_DEFS =
{
  "number" => { default_scope: :window, type: :bool, default: false },
  "relativenumber" => { default_scope: :window, type: :bool, default: false },
  "wrap" => { default_scope: :window, type: :bool, default: true },
  "linebreak" => { default_scope: :window, type: :bool, default: false },
  "breakindent" => { default_scope: :window, type: :bool, default: false },
  "cursorline" => { default_scope: :window, type: :bool, default: false },
  "scrolloff" => { default_scope: :window, type: :int, default: 0 },
  "sidescrolloff" => { default_scope: :window, type: :int, default: 0 },
  "numberwidth" => { default_scope: :window, type: :int, default: 4 },
  "colorcolumn" => { default_scope: :window, type: :string, default: nil },
  "signcolumn" => { default_scope: :window, type: :string, default: "auto" },
  "list" => { default_scope: :window, type: :bool, default: false },
  "listchars" => { default_scope: :window, type: :string, default: "tab:>-,trail:-,nbsp:+" },
  "showbreak" => { default_scope: :window, type: :string, default: ">" },
  "showmatch" => { default_scope: :global, type: :bool, default: false },
  "matchtime" => { default_scope: :global, type: :int, default: 5 },
  "whichwrap" => { default_scope: :global, type: :string, default: "" },
  "virtualedit" => { default_scope: :global, type: :string, default: "" },
  "ignorecase" => { default_scope: :global, type: :bool, default: false },
  "smartcase" => { default_scope: :global, type: :bool, default: false },
  "hlsearch" => { default_scope: :global, type: :bool, default: true },
  "incsearch" => { default_scope: :global, type: :bool, default: false },
  "splitbelow" => { default_scope: :global, type: :bool, default: false },
  "splitright" => { default_scope: :global, type: :bool, default: false },
  "hidden" => { default_scope: :global, type: :bool, default: false },
  "autowrite" => { default_scope: :global, type: :bool, default: false },
  "clipboard" => { default_scope: :global, type: :string, default: "" },
  "timeoutlen" => { default_scope: :global, type: :int, default: 1000 },
  "ttimeoutlen" => { default_scope: :global, type: :int, default: 50 },
  "backspace" => { default_scope: :global, type: :string, default: "indent,eol,start" },
  "completeopt" => { default_scope: :global, type: :string, default: "menu,menuone,noselect" },
  "pumheight" => { default_scope: :global, type: :int, default: 10 },
  "wildmode" => { default_scope: :global, type: :string, default: "full" },
  "wildignore" => { default_scope: :global, type: :string, default: "" },
  "wildignorecase" => { default_scope: :global, type: :bool, default: false },
  "wildmenu" => { default_scope: :global, type: :bool, default: false },
  "termguicolors" => { default_scope: :global, type: :bool, default: false },
  "path" => { default_scope: :buffer, type: :string, default: nil },
  "suffixesadd" => { default_scope: :buffer, type: :string, default: nil },
  "textwidth" => { default_scope: :buffer, type: :int, default: 0 },
  "formatoptions" => { default_scope: :buffer, type: :string, default: nil },
  "expandtab" => { default_scope: :buffer, type: :bool, default: false },
  "shiftwidth" => { default_scope: :buffer, type: :int, default: 2 },
  "softtabstop" => { default_scope: :buffer, type: :int, default: 0 },
  "autoindent" => { default_scope: :buffer, type: :bool, default: true },
  "smartindent" => { default_scope: :buffer, type: :bool, default: true },
  "iskeyword" => { default_scope: :buffer, type: :string, default: nil },
  "tabstop" => { default_scope: :buffer, type: :int, default: 2 },
  "filetype" => { default_scope: :buffer, type: :string, default: nil },
  "onsavehook" => { default_scope: :buffer, type: :bool, default: true },
  "grepprg" => { default_scope: :global, type: :string, default: "grep -nH" },
  "grepformat" => { default_scope: :global, type: :string, default: "%f:%l:%m" },
  "runprg" => { default_scope: :buffer, type: :string, default: nil }
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEditor

Returns a new instance of Editor.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/ruvim/editor.rb', line 64

def initialize
  @buffers = {}
  @windows = {}
  @layout_tree = nil
  @tabpages = []
  @current_tabpage_index = nil
  @next_tabpage_id = 1
  @suspend_tab_autosave = false
  @next_buffer_id = 1
  @next_window_id = 1
  @current_window_id = nil
  @alternate_buffer_id = nil
  @mode = :normal
  @message = ""
  @message_kind = :info
  @message_deadline = nil
  @pending_count = nil
  @restricted_mode = false
  @current_window_view_height_hint = 1
  @running = true
  @open_path_handler = nil
  @keymap_manager = nil
  @app_action_handler = nil
  @global_options = default_global_options
  @command_line = CommandLine.new
  @last_search = nil
  @hlsearch_suppressed = false
  @last_find = nil
  @registers = {}
  @active_register_name = nil
  @local_marks = Hash.new { |h, k| h[k] = {} }
  @global_marks = {}
  @jumplist = []
  @jump_index = nil
  @macros = {}
  @macro_recording = nil
  @visual_state = nil
  @rich_state = nil
  @quickfix_list = { items: [], index: nil }
  @location_lists = Hash.new { |h, k| h[k] = { items: [], index: nil } }
  @arglist = []
  @arglist_index = 0
  @hit_enter_lines = nil
  @run_history = {}  # buffer_id => last run command (unexpanded)
  @run_output_buffer_id = nil
end

Instance Attribute Details

#alternate_buffer_idObject

Returns the value of attribute alternate_buffer_id.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def alternate_buffer_id
  @alternate_buffer_id
end

#app_action_handlerObject

Returns the value of attribute app_action_handler.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def app_action_handler
  @app_action_handler
end

#buffersObject (readonly)

Returns the value of attribute buffers.



61
62
63
# File 'lib/ruvim/editor.rb', line 61

def buffers
  @buffers
end

#current_window_idObject

Returns the value of attribute current_window_id.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def current_window_id
  @current_window_id
end

#current_window_view_height_hintObject

Returns the value of attribute current_window_view_height_hint.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def current_window_view_height_hint
  @current_window_view_height_hint
end

#git_stream_handlerObject

Returns the value of attribute git_stream_handler.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def git_stream_handler
  @git_stream_handler
end

#git_stream_stop_handlerObject

Returns the value of attribute git_stream_stop_handler.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def git_stream_stop_handler
  @git_stream_stop_handler
end

#keymap_managerObject

Returns the value of attribute keymap_manager.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def keymap_manager
  @keymap_manager
end

#layout_treeObject (readonly)

Returns the value of attribute layout_tree.



61
62
63
# File 'lib/ruvim/editor.rb', line 61

def layout_tree
  @layout_tree
end

#messageObject

Returns the value of attribute message.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def message
  @message
end

#modeObject

Returns the value of attribute mode.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def mode
  @mode
end

#open_path_handlerObject

Returns the value of attribute open_path_handler.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def open_path_handler
  @open_path_handler
end

#pending_countObject

Returns the value of attribute pending_count.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def pending_count
  @pending_count
end

#restricted_modeObject

Returns the value of attribute restricted_mode.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def restricted_mode
  @restricted_mode
end

#run_stream_handlerObject

Returns the value of attribute run_stream_handler.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def run_stream_handler
  @run_stream_handler
end

#shell_executorObject

Returns the value of attribute shell_executor.



62
63
64
# File 'lib/ruvim/editor.rb', line 62

def shell_executor
  @shell_executor
end

#windowsObject (readonly)

Returns the value of attribute windows.



61
62
63
# File 'lib/ruvim/editor.rb', line 61

def windows
  @windows
end

Instance Method Details

#active_register_nameObject



334
335
336
# File 'lib/ruvim/editor.rb', line 334

def active_register_name
  @active_register_name
end

#add_buffer_from_file(path) ⇒ Object



586
587
588
589
590
591
592
# File 'lib/ruvim/editor.rb', line 586

def add_buffer_from_file(path)
  id = next_buffer_id
  buffer = Buffer.from_file(id:, path:)
  assign_detected_filetype(buffer)
  @buffers[id] = buffer
  buffer
end

#add_empty_buffer(path: nil) ⇒ Object



570
571
572
573
574
575
576
# File 'lib/ruvim/editor.rb', line 570

def add_empty_buffer(path: nil)
  id = next_buffer_id
  buffer = Buffer.new(id:, path:)
  assign_detected_filetype(buffer)
  @buffers[id] = buffer
  buffer
end

#add_virtual_buffer(kind:, name:, lines:, filetype: nil, readonly: true, modifiable: false) ⇒ Object



578
579
580
581
582
583
584
# File 'lib/ruvim/editor.rb', line 578

def add_virtual_buffer(kind:, name:, lines:, filetype: nil, readonly: true, modifiable: false)
  id = next_buffer_id
  buffer = Buffer.new(id:, lines:, kind:, name:, readonly:, modifiable:)
  assign_filetype(buffer, filetype) if filetype
  @buffers[id] = buffer
  buffer
end

#add_window(buffer_id:) ⇒ Object



594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
# File 'lib/ruvim/editor.rb', line 594

def add_window(buffer_id:)
  id = next_window_id
  window = Window.new(id:, buffer_id:)
  @windows[id] = window
  leaf = { type: :window, id: id }
  if @layout_tree.nil?
    @layout_tree = leaf
  else
    # Append as sibling — used for initial bootstrap only
    if @layout_tree[:type] == :window
      @layout_tree = { type: :hsplit, children: [@layout_tree, leaf] }
    else
      @layout_tree[:children] << leaf
    end
  end
  @current_window_id ||= id
  ensure_initial_tabpage!
  save_current_tabpage_state! unless @suspend_tab_autosave
  window
end

#apply_filetype_defaults(buffer, ft) ⇒ Object



1148
1149
1150
1151
1152
1153
# File 'lib/ruvim/editor.rb', line 1148

def apply_filetype_defaults(buffer, ft)
  unless buffer.options.key?("runprg")
    runprg = filetype_default_runprg(ft)
    buffer.options["runprg"] = runprg if runprg
  end
end

#arglistObject



1305
1306
1307
# File 'lib/ruvim/editor.rb', line 1305

def arglist
  @arglist.dup
end

#arglist_currentObject



1318
1319
1320
# File 'lib/ruvim/editor.rb', line 1318

def arglist_current
  @arglist[@arglist_index] if @arglist_index < @arglist.length
end

#arglist_firstObject



1340
1341
1342
1343
# File 'lib/ruvim/editor.rb', line 1340

def arglist_first
  @arglist_index = 0
  @arglist[@arglist_index] if @arglist.length > 0
end

#arglist_indexObject



1309
1310
1311
# File 'lib/ruvim/editor.rb', line 1309

def arglist_index
  @arglist_index
end

#arglist_lastObject



1345
1346
1347
1348
# File 'lib/ruvim/editor.rb', line 1345

def arglist_last
  @arglist_index = [@arglist.length - 1, 0].max
  @arglist[@arglist_index] if @arglist.length > 0
end

#arglist_next(count = 1) ⇒ Object



1322
1323
1324
1325
1326
1327
1328
1329
# File 'lib/ruvim/editor.rb', line 1322

def arglist_next(count = 1)
  new_index = @arglist_index + count
  if new_index >= @arglist.length
    raise RuVim::CommandError, "Already at last argument"
  end
  @arglist_index = new_index
  @arglist[@arglist_index]
end

#arglist_prev(count = 1) ⇒ Object



1331
1332
1333
1334
1335
1336
1337
1338
# File 'lib/ruvim/editor.rb', line 1331

def arglist_prev(count = 1)
  new_index = @arglist_index - count
  if new_index < 0
    raise RuVim::CommandError, "Already at first argument"
  end
  @arglist_index = new_index
  @arglist[@arglist_index]
end

#assign_filetype(buffer, ft) ⇒ Object



1142
1143
1144
1145
1146
# File 'lib/ruvim/editor.rb', line 1142

def assign_filetype(buffer, ft)
  buffer.options["filetype"] = ft
  buffer.lang_module = resolve_lang_module(ft)
  apply_filetype_defaults(buffer, ft)
end

#buffer_idsObject



818
819
820
# File 'lib/ruvim/editor.rb', line 818

def buffer_ids
  @buffers.keys
end

#cancel_command_lineObject



1058
1059
1060
1061
# File 'lib/ruvim/editor.rb', line 1058

def cancel_command_line
  @command_line.clear
  leave_command_line
end

#clear_expired_transient_message!(now: nil) ⇒ Object



1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
# File 'lib/ruvim/editor.rb', line 1114

def clear_expired_transient_message!(now: nil)
  return false unless @message_deadline
  return false if message_error?
  return false if command_line_active?

  now ||= Process.clock_gettime(Process::CLOCK_MONOTONIC)
  return false if now < @message_deadline

  clear_message
  true
rescue StandardError
  false
end

#clear_messageObject



1093
1094
1095
1096
1097
# File 'lib/ruvim/editor.rb', line 1093

def clear_message
  @message_kind = :info
  @message = ""
  @message_deadline = nil
end

#clear_visualObject



476
477
478
# File 'lib/ruvim/editor.rb', line 476

def clear_visual
  @visual_state = nil
end

#close_current_tabpageObject



661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
# File 'lib/ruvim/editor.rb', line 661

def close_current_tabpage
  ensure_initial_tabpage!
  return nil if @tabpages.length <= 1

  save_current_tabpage_state!
  removed = @tabpages.delete_at(@current_tabpage_index)
  removed_tree = removed && removed[:layout_tree]
  tree_leaves(removed_tree).each do |wid|
    @windows.delete(wid)
    @location_lists.delete(wid)
  end
  @current_tabpage_index = [@current_tabpage_index, @tabpages.length - 1].min
  load_tabpage_state!(@tabpages[@current_tabpage_index])
  current_window
end

#close_current_windowObject



637
638
639
# File 'lib/ruvim/editor.rb', line 637

def close_current_window
  close_window(@current_window_id)
end

#close_window(id) ⇒ Object



641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
# File 'lib/ruvim/editor.rb', line 641

def close_window(id)
  leaves = tree_leaves(@layout_tree)
  return nil if leaves.empty?
  return nil if leaves.length <= 1
  return nil unless leaves.include?(id)

  save_current_tabpage_state! unless @suspend_tab_autosave
  idx = leaves.index(id) || 0
  @windows.delete(id)
  @location_lists.delete(id)

  @layout_tree = tree_remove(@layout_tree, id)

  new_leaves = tree_leaves(@layout_tree)
  @current_window_id = new_leaves[[idx, new_leaves.length - 1].min] if @current_window_id == id
  @current_window_id ||= new_leaves.first
  save_current_tabpage_state! unless @suspend_tab_autosave
  current_window
end

#command_area_active?Boolean

Returns:

  • (Boolean)


1138
1139
1140
# File 'lib/ruvim/editor.rb', line 1138

def command_area_active?
  command_line_active? || message_error?
end

#command_lineObject



135
136
137
# File 'lib/ruvim/editor.rb', line 135

def command_line
  @command_line
end

#command_line_active?Boolean

Returns:

  • (Boolean)


1134
1135
1136
# File 'lib/ruvim/editor.rb', line 1134

def command_line_active?
  @mode == :command_line
end

#command_line_prefixObject



143
144
145
# File 'lib/ruvim/editor.rb', line 143

def command_line_prefix
  @command_line.prefix
end

#consume_active_register(default = "\"") ⇒ Object



338
339
340
341
342
# File 'lib/ruvim/editor.rb', line 338

def consume_active_register(default = "\"")
  name = @active_register_name || default
  @active_register_name = nil
  name
end

#current_bufferObject



176
177
178
# File 'lib/ruvim/editor.rb', line 176

def current_buffer
  @buffers.fetch(current_window.buffer_id)
end

#current_locationObject



394
395
396
# File 'lib/ruvim/editor.rb', line 394

def current_location
  { buffer_id: current_buffer.id, row: current_window.cursor_y, col: current_window.cursor_x }
end

#current_location_list_item(window_id = current_window_id) ⇒ Object



960
961
962
963
964
# File 'lib/ruvim/editor.rb', line 960

def current_location_list_item(window_id = current_window_id)
  list = location_list(window_id)
  idx = list[:index]
  idx ? list[:items][idx] : nil
end

#current_quickfix_itemObject



919
920
921
922
# File 'lib/ruvim/editor.rb', line 919

def current_quickfix_item
  idx = @quickfix_list[:index]
  idx ? @quickfix_list[:items][idx] : nil
end

#current_tabpage_indexObject



885
886
887
# File 'lib/ruvim/editor.rb', line 885

def current_tabpage_index
  @current_tabpage_index || 0
end

#current_tabpage_numberObject



889
890
891
# File 'lib/ruvim/editor.rb', line 889

def current_tabpage_number
  current_tabpage_index + 1
end

#current_windowObject



172
173
174
# File 'lib/ruvim/editor.rb', line 172

def current_window
  @windows.fetch(@current_window_id)
end

#delete_buffer(buffer_id) ⇒ Object



830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
# File 'lib/ruvim/editor.rb', line 830

def delete_buffer(buffer_id)
  buffer = @buffers[buffer_id]
  return nil unless buffer

  if @buffers.length <= 1
    replacement = add_empty_buffer
  else
    replacement = nil
  end

  fallback_id =
    if replacement
      replacement.id
    else
      candidates = @buffers.keys.reject { |bid| bid == buffer_id }
      alt = @alternate_buffer_id
      (alt && alt != buffer_id && @buffers.key?(alt)) ? alt : candidates.first
    end

  @windows.each_value do |win|
    next unless win.buffer_id == buffer_id
    next unless fallback_id

    win.buffer_id = fallback_id
    win.cursor_x = 0
    win.cursor_y = 0
    win.row_offset = 0
    win.col_offset = 0
  end

  @buffers.delete(buffer_id)
  @local_marks.delete(buffer_id)
  @alternate_buffer_id = nil if @alternate_buffer_id == buffer_id
  save_current_tabpage_state! unless @suspend_tab_autosave
  ensure_bootstrap_buffer! if @buffers.empty?
  true
end

#detect_filetype(path) ⇒ Object



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/ruvim/editor.rb', line 260

def detect_filetype(path)
  p = path.to_s
  return nil if p.empty?

  base = File.basename(p)

  # Basename-based detection (exact match, then prefix)
  base_ft = Lang::Registry.detect_by_basename(base)
  return base_ft if base_ft

  # Extension-based detection
  ext_ft = Lang::Registry.detect_by_extension(File.extname(base))
  return ext_ft if ext_ft

  detect_filetype_from_shebang(p)
end

#echo(msg) ⇒ Object



1072
1073
1074
1075
1076
# File 'lib/ruvim/editor.rb', line 1072

def echo(msg)
  @message_kind = :info
  @message = msg.to_s
  @message_deadline = nil
end

#echo_error(msg) ⇒ Object



1087
1088
1089
1090
1091
# File 'lib/ruvim/editor.rb', line 1087

def echo_error(msg)
  @message_kind = :error
  @message = msg.to_s
  @message_deadline = nil
end

#echo_multiline(lines) ⇒ Object



561
562
563
564
565
566
567
568
# File 'lib/ruvim/editor.rb', line 561

def echo_multiline(lines)
  lines = Array(lines)
  if lines.length <= 1
    echo(lines.first.to_s)
  else
    enter_hit_enter_mode(lines)
  end
end

#echo_temporary(msg, duration_seconds:) ⇒ Object



1078
1079
1080
1081
1082
1083
1084
1085
# File 'lib/ruvim/editor.rb', line 1078

def echo_temporary(msg, duration_seconds:)
  @message_kind = :info
  @message = msg.to_s
  dur = duration_seconds.to_f
  @message_deadline = dur.positive? ? (Process.clock_gettime(Process::CLOCK_MONOTONIC) + dur) : nil
rescue StandardError
  @message_deadline = nil
end

#effective_option(name, window: current_window, buffer: current_buffer) ⇒ Object



203
204
205
206
207
208
209
210
211
212
# File 'lib/ruvim/editor.rb', line 203

def effective_option(name, window: current_window, buffer: current_buffer)
  key = name.to_s
  if window && window.options.key?(key)
    window.options[key]
  elsif buffer && buffer.options.key?(key)
    buffer.options[key]
  else
    @global_options[key]
  end
end

#ensure_bootstrap_buffer!Object



765
766
767
768
769
770
771
# File 'lib/ruvim/editor.rb', line 765

def ensure_bootstrap_buffer!
  return unless @buffers.empty?

  buffer = add_empty_buffer
  add_window(buffer_id: buffer.id)
  ensure_initial_tabpage!
end

#enter_command_line_mode(prefix = ":") ⇒ Object



1052
1053
1054
1055
1056
# File 'lib/ruvim/editor.rb', line 1052

def enter_command_line_mode(prefix = ":")
  @mode = :command_line
  @command_line.reset(prefix:)
  @pending_count = nil
end

#enter_hit_enter_mode(lines) ⇒ Object



550
551
552
553
554
# File 'lib/ruvim/editor.rb', line 550

def enter_hit_enter_mode(lines)
  @mode = :hit_enter
  @hit_enter_lines = Array(lines)
  @pending_count = nil
end

#enter_insert_modeObject



1047
1048
1049
1050
# File 'lib/ruvim/editor.rb', line 1047

def enter_insert_mode
  @mode = :insert
  @pending_count = nil
end

#enter_normal_modeObject



1040
1041
1042
1043
1044
1045
# File 'lib/ruvim/editor.rb', line 1040

def enter_normal_mode
  @mode = :normal
  @pending_count = nil
  clear_visual
  @rich_state = nil
end

#enter_rich_mode(format:, delimiter:) ⇒ Object



531
532
533
534
535
# File 'lib/ruvim/editor.rb', line 531

def enter_rich_mode(format:, delimiter:)
  @mode = :rich
  @pending_count = nil
  @rich_state = { format: format, delimiter: delimiter }
end

#enter_visual(mode) ⇒ Object



466
467
468
469
470
471
472
473
474
# File 'lib/ruvim/editor.rb', line 466

def enter_visual(mode)
  @mode = mode
  @visual_state = {
    mode: mode,
    anchor_y: current_window.cursor_y,
    anchor_x: current_window.cursor_x
  }
  @pending_count = nil
end

#exit_hit_enter_modeObject



556
557
558
559
# File 'lib/ruvim/editor.rb', line 556

def exit_hit_enter_mode
  @hit_enter_lines = nil
  enter_normal_mode
end

#exit_rich_modeObject



537
538
539
540
# File 'lib/ruvim/editor.rb', line 537

def exit_rich_mode
  @rich_state = nil
  enter_normal_mode
end

#filetype_default_runprg(ft) ⇒ Object



1155
1156
1157
# File 'lib/ruvim/editor.rb', line 1155

def filetype_default_runprg(ft)
  Lang::Registry.runprg_for(ft)
end

#find_window_ids_by_buffer_kind(kind) ⇒ Object



1003
1004
1005
1006
1007
1008
1009
# File 'lib/ruvim/editor.rb', line 1003

def find_window_ids_by_buffer_kind(kind)
  window_order.select do |wid|
    win = @windows[wid]
    buf = win && @buffers[win.buffer_id]
    buf && buf.kind == kind
  end
end

#focus_next_windowObject



685
686
687
688
689
690
691
# File 'lib/ruvim/editor.rb', line 685

def focus_next_window
  order = window_order
  return current_window if order.length <= 1

  idx = order.index(@current_window_id) || 0
  focus_window(order[(idx + 1) % order.length])
end

#focus_prev_windowObject



693
694
695
696
697
698
699
# File 'lib/ruvim/editor.rb', line 693

def focus_prev_window
  order = window_order
  return current_window if order.length <= 1

  idx = order.index(@current_window_id) || 0
  focus_window(order[(idx - 1) % order.length])
end

#focus_window(id) ⇒ Object



677
678
679
680
681
682
683
# File 'lib/ruvim/editor.rb', line 677

def focus_window(id)
  return nil unless @windows.key?(id)

  @current_window_id = id
  save_current_tabpage_state! unless @suspend_tab_autosave
  current_window
end

#focus_window_direction(dir) ⇒ Object



701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
# File 'lib/ruvim/editor.rb', line 701

def focus_window_direction(dir)
  leaves = tree_leaves(@layout_tree)
  return current_window if leaves.length <= 1

  rects = tree_compute_rects(@layout_tree, top: 0.0, left: 0.0, height: 1.0, width: 1.0)
  cur = rects[@current_window_id]
  return current_window unless cur

  best_id = nil
  best_dist = Float::INFINITY
  cur_cx = cur[:left] + cur[:width] / 2.0
  cur_cy = cur[:top] + cur[:height] / 2.0

  rects.each do |wid, r|
    next if wid == @current_window_id
    rcx = r[:left] + r[:width] / 2.0
    rcy = r[:top] + r[:height] / 2.0

    in_direction = case dir
                   when :left  then rcx < cur_cx
                   when :right then rcx > cur_cx
                   when :up    then rcy < cur_cy
                   when :down  then rcy > cur_cy
                   end
    next unless in_direction

    dist = (rcx - cur_cx).abs + (rcy - cur_cy).abs
    if dist < best_dist
      best_dist = dist
      best_id = wid
    end
  end

  best_id ? focus_window(best_id) : current_window
end

#get_option(name, scope: :effective, window: current_window, buffer: current_buffer) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/ruvim/editor.rb', line 214

def get_option(name, scope: :effective, window: current_window, buffer: current_buffer)
  key = name.to_s
  case scope
  when :global
    @global_options[key]
  when :buffer
    buffer&.options&.[](key)
  when :window
    window&.options&.[](key)
  else
    effective_option(key, window:, buffer:)
  end
end

#get_register(name = "\"") ⇒ Object



315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/ruvim/editor.rb', line 315

def get_register(name = "\"")
  key = name.to_s.downcase
  if key == "\""
    if (default_clip = clipboard_default_register_key)
      if (payload = read_clipboard_register(default_clip))
        @registers["\""] = dup_register_payload(payload)
        return @registers["\""]
      end
    end
  end
  return read_clipboard_register(key) if clipboard_register?(key)

  @registers[key]
end

#global_optionsObject



139
140
141
# File 'lib/ruvim/editor.rb', line 139

def global_options
  @global_options
end

#has_split_ancestor_on_axis?(dir) ⇒ Boolean

Check if the path from root to the current window passes through a split node matching the direction's axis. Used by focus_or_split to decide whether to split or stay put at edges. left/right → check for :vsplit ancestor up/down → check for :hsplit ancestor

Returns:

  • (Boolean)


995
996
997
998
999
1000
1001
# File 'lib/ruvim/editor.rb', line 995

def has_split_ancestor_on_axis?(dir)
  target_type = case dir
                when :left, :right then :vsplit
                when :up, :down then :hsplit
                end
  tree_path_has_split_type?(@layout_tree, @current_window_id, target_type)
end

#hit_enter_active?Boolean

Returns:

  • (Boolean)


542
543
544
# File 'lib/ruvim/editor.rb', line 542

def hit_enter_active?
  @mode == :hit_enter
end

#hit_enter_linesObject



546
547
548
# File 'lib/ruvim/editor.rb', line 546

def hit_enter_lines
  @hit_enter_lines
end

#hlsearch_suppressed?Boolean

Returns:

  • (Boolean)


164
165
166
# File 'lib/ruvim/editor.rb', line 164

def hlsearch_suppressed?
  @hlsearch_suppressed
end

#invoke_app_action(name, **kwargs) ⇒ Object



187
188
189
190
191
192
193
# File 'lib/ruvim/editor.rb', line 187

def invoke_app_action(name, **kwargs)
  handler = @app_action_handler
  return false unless handler

  handler.call(name, **kwargs)
  true
end

#jump_newer(linewise: false) ⇒ Object



445
446
447
448
449
450
451
452
453
# File 'lib/ruvim/editor.rb', line 445

def jump_newer(linewise: false)
  return nil if @jumplist.empty? || @jump_index.nil?

  next_idx = @jump_index + 1
  return nil if next_idx >= @jumplist.length

  @jump_index = next_idx
  jump_to_location(@jumplist[@jump_index], linewise:)
end

#jump_older(linewise: false) ⇒ Object



434
435
436
437
438
439
440
441
442
443
# File 'lib/ruvim/editor.rb', line 434

def jump_older(linewise: false)
  return nil if @jumplist.empty?

  if @jump_index.nil?
    push_jump_location(current_location)
  else
    @jump_index = [@jump_index - 1, 0].max
  end
  jump_to_location(@jumplist[@jump_index], linewise:)
end

#jump_to_location(loc, linewise: false) ⇒ Object



1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
# File 'lib/ruvim/editor.rb', line 1291

def jump_to_location(loc, linewise: false)
  location = normalize_location(loc)
  return nil unless location
  return nil unless @buffers.key?(location[:buffer_id])

  switch_to_buffer(location[:buffer_id]) if current_buffer.id != location[:buffer_id]
  current_window.cursor_y = location[:row]
  current_window.cursor_x = linewise ? 0 : location[:col]
  current_window.clamp_to_buffer(current_buffer)
  current_window.cursor_x = first_nonblank_col(current_buffer, current_window.cursor_y) if linewise
  current_window.clamp_to_buffer(current_buffer)
  current_location
end

#jump_to_mark(name, linewise: false) ⇒ Object



455
456
457
458
459
460
# File 'lib/ruvim/editor.rb', line 455

def jump_to_mark(name, linewise: false)
  loc = mark_location(name)
  return nil unless loc

  jump_to_location(loc, linewise:)
end

#last_findObject



151
152
153
# File 'lib/ruvim/editor.rb', line 151

def last_find
  @last_find
end

#last_searchObject



147
148
149
# File 'lib/ruvim/editor.rb', line 147

def last_search
  @last_search
end

#leave_command_lineObject



1063
1064
1065
1066
1067
1068
1069
1070
# File 'lib/ruvim/editor.rb', line 1063

def leave_command_line
  if @rich_state
    @mode = :rich
    @pending_count = nil
  else
    enter_normal_mode
  end
end

#location_items(window_id = current_window_id) ⇒ Object



950
951
952
# File 'lib/ruvim/editor.rb', line 950

def location_items(window_id = current_window_id)
  location_list(window_id)[:items]
end

#location_list(window_id = current_window_id) ⇒ Object



946
947
948
# File 'lib/ruvim/editor.rb', line 946

def location_list(window_id = current_window_id)
  @location_lists[window_id]
end

#macro_keys(name) ⇒ Object



390
391
392
# File 'lib/ruvim/editor.rb', line 390

def macro_keys(name)
  @macros[name.to_s.downcase]
end

#macro_recording?Boolean

Returns:

  • (Boolean)


352
353
354
# File 'lib/ruvim/editor.rb', line 352

def macro_recording?
  !@macro_recording.nil?
end

#macro_recording_nameObject



356
357
358
# File 'lib/ruvim/editor.rb', line 356

def macro_recording_name
  @macro_recording && @macro_recording[:name]
end

#macrosObject



348
349
350
# File 'lib/ruvim/editor.rb', line 348

def macros
  @macros
end

#mark_location(name, buffer_id: current_buffer.id) ⇒ Object



411
412
413
414
415
416
417
418
419
420
# File 'lib/ruvim/editor.rb', line 411

def mark_location(name, buffer_id: current_buffer.id)
  mark = name.to_s
  return nil unless mark.match?(/\A[A-Za-z]\z/)

  if mark.match?(/\A[a-z]\z/)
    @local_marks[buffer_id][mark]
  else
    @global_marks[mark]
  end
end

#materialize_intro_buffer!Object



806
807
808
809
810
811
812
813
814
815
816
# File 'lib/ruvim/editor.rb', line 806

def materialize_intro_buffer!
  return false unless current_buffer.intro_buffer?

  current_buffer.become_normal_empty_buffer!
  assign_filetype(current_buffer, nil)
  current_window.cursor_x = 0
  current_window.cursor_y = 0
  current_window.row_offset = 0
  current_window.col_offset = 0
  true
end

#message_error?Boolean

Returns:

  • (Boolean)


1099
1100
1101
# File 'lib/ruvim/editor.rb', line 1099

def message_error?
  !@message.to_s.empty? && @message_kind == :error
end

#move_location_list(step, window_id: current_window_id) ⇒ Object



966
967
968
969
970
971
972
973
974
975
976
977
978
# File 'lib/ruvim/editor.rb', line 966

def move_location_list(step, window_id: current_window_id)
  list = location_list(window_id)
  items = list[:items]
  return nil if items.empty?

  cur = list[:index]
  list[:index] = if cur.nil?
                   step.to_i > 0 ? 0 : items.length - 1
                 else
                   (cur + step.to_i) % items.length
                 end
  current_location_list_item(window_id)
end

#move_quickfix(step) ⇒ Object



924
925
926
927
928
929
930
931
932
933
934
935
# File 'lib/ruvim/editor.rb', line 924

def move_quickfix(step)
  items = @quickfix_list[:items]
  return nil if items.empty?

  cur = @quickfix_list[:index]
  @quickfix_list[:index] = if cur.nil?
                              step.to_i > 0 ? 0 : items.length - 1
                            else
                              (cur + step.to_i) % items.length
                            end
  current_quickfix_item
end

#next_buffer_id_from(current_id, step = 1) ⇒ Object



822
823
824
825
826
827
828
# File 'lib/ruvim/editor.rb', line 822

def next_buffer_id_from(current_id, step = 1)
  ids = buffer_ids
  return nil if ids.empty?

  idx = ids.index(current_id) || 0
  ids[(idx + step) % ids.length]
end

#open_path(path) ⇒ Object



751
752
753
754
755
756
# File 'lib/ruvim/editor.rb', line 751

def open_path(path)
  handler = @open_path_handler
  return handler.call(path) if handler

  open_path_sync(path)
end

#open_path_sync(path) ⇒ Object



758
759
760
761
762
763
# File 'lib/ruvim/editor.rb', line 758

def open_path_sync(path)
  buffer = add_buffer_from_file(path)
  switch_to_buffer(buffer.id)
  echo("[New File]") unless path && File.exist?(path)
  buffer
end

#option_def(name) ⇒ Object



195
196
197
# File 'lib/ruvim/editor.rb', line 195

def option_def(name)
  OPTION_DEFS[name.to_s]
end

#option_default_scope(name) ⇒ Object



199
200
201
# File 'lib/ruvim/editor.rb', line 199

def option_default_scope(name)
  option_def(name)&.fetch(:default_scope, :global) || :global
end

#option_snapshot(window: current_window, buffer: current_buffer) ⇒ Object



247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/ruvim/editor.rb', line 247

def option_snapshot(window: current_window, buffer: current_buffer)
  keys = (OPTION_DEFS.keys + @global_options.keys + (buffer&.options&.keys || []) + (window&.options&.keys || [])).uniq.sort
  keys.map do |k|
    {
      name: k,
      effective: get_option(k, scope: :effective, window:, buffer:),
      global: get_option(k, scope: :global, window:, buffer:),
      buffer: get_option(k, scope: :buffer, window:, buffer:),
      window: get_option(k, scope: :window, window:, buffer:)
    }
  end
end

#push_jump_location(location = current_location) ⇒ Object



422
423
424
425
426
427
428
429
430
431
432
# File 'lib/ruvim/editor.rb', line 422

def push_jump_location(location = current_location)
  loc = normalize_location(location)
  return nil unless loc

  if @jump_index && @jump_index < @jumplist.length - 1
    @jumplist = @jumplist[0..@jump_index]
  end
  @jumplist << loc unless same_location?(@jumplist.last, loc)
  @jump_index = @jumplist.length - 1 unless @jumplist.empty?
  loc
end

#quickfix_indexObject



909
910
911
# File 'lib/ruvim/editor.rb', line 909

def quickfix_index
  @quickfix_list[:index]
end

#quickfix_itemsObject



905
906
907
# File 'lib/ruvim/editor.rb', line 905

def quickfix_items
  @quickfix_list[:items]
end

#record_macro_key(key) ⇒ Object



384
385
386
387
388
# File 'lib/ruvim/editor.rb', line 384

def record_macro_key(key)
  return unless @macro_recording

  @macro_recording[:keys] << dup_macro_key(key)
end

#registersObject



277
278
279
# File 'lib/ruvim/editor.rb', line 277

def registers
  @registers
end

#request_quit!Object



119
120
121
# File 'lib/ruvim/editor.rb', line 119

def request_quit!
  @running = false
end

#restricted_mode?Boolean

Returns:

  • (Boolean)


115
116
117
# File 'lib/ruvim/editor.rb', line 115

def restricted_mode?
  !!@restricted_mode
end

#rich_mode?Boolean

Returns:

  • (Boolean)


527
528
529
# File 'lib/ruvim/editor.rb', line 527

def rich_mode?
  @mode == :rich
end

#rich_stateObject



523
524
525
# File 'lib/ruvim/editor.rb', line 523

def rich_state
  @rich_state
end

#run_historyObject



123
124
125
# File 'lib/ruvim/editor.rb', line 123

def run_history
  @run_history
end

#run_output_buffer_idObject



127
128
129
# File 'lib/ruvim/editor.rb', line 127

def run_output_buffer_id
  @run_output_buffer_id
end

#run_output_buffer_id=(id) ⇒ Object



131
132
133
# File 'lib/ruvim/editor.rb', line 131

def run_output_buffer_id=(id)
  @run_output_buffer_id = id
end

#running?Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/ruvim/editor.rb', line 111

def running?
  @running
end

#select_location_list(index, window_id: current_window_id) ⇒ Object



980
981
982
983
984
985
986
987
988
# File 'lib/ruvim/editor.rb', line 980

def select_location_list(index, window_id: current_window_id)
  list = location_list(window_id)
  items = list[:items]
  return nil if items.empty?

  i = [[index.to_i, 0].max, items.length - 1].min
  list[:index] = i
  current_location_list_item(window_id)
end

#select_quickfix(index) ⇒ Object



937
938
939
940
941
942
943
944
# File 'lib/ruvim/editor.rb', line 937

def select_quickfix(index)
  items = @quickfix_list[:items]
  return nil if items.empty?

  i = [[index.to_i, 0].max, items.length - 1].min
  @quickfix_list[:index] = i
  current_quickfix_item
end

#set_active_register(name) ⇒ Object



330
331
332
# File 'lib/ruvim/editor.rb', line 330

def set_active_register(name)
  @active_register_name = name.to_s
end

#set_arglist(paths) ⇒ Object



1313
1314
1315
1316
# File 'lib/ruvim/editor.rb', line 1313

def set_arglist(paths)
  @arglist = Array(paths).dup
  @arglist_index = 0
end

#set_last_find(char:, direction:, till:) ⇒ Object



168
169
170
# File 'lib/ruvim/editor.rb', line 168

def set_last_find(char:, direction:, till:)
  @last_find = { char: char, direction: direction, till: !!till }
end

#set_last_search(pattern:, direction:) ⇒ Object



155
156
157
158
# File 'lib/ruvim/editor.rb', line 155

def set_last_search(pattern:, direction:)
  @last_search = { pattern: pattern, direction: direction }
  @hlsearch_suppressed = false
end

#set_location_list(items, window_id: current_window_id) ⇒ Object



954
955
956
957
958
# File 'lib/ruvim/editor.rb', line 954

def set_location_list(items, window_id: current_window_id)
  ary = Array(items).map { |it| normalize_location(it)&.merge(text: (it[:text] || it["text"]).to_s) }.compact
  @location_lists[window_id] = { items: ary, index: nil }
  @location_lists[window_id]
end

#set_mark(name, window = current_window) ⇒ Object



398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/ruvim/editor.rb', line 398

def set_mark(name, window = current_window)
  mark = name.to_s
  return false unless mark.match?(/\A[A-Za-z]\z/)

  loc = { buffer_id: window.buffer_id, row: window.cursor_y, col: window.cursor_x }
  if mark.match?(/\A[a-z]\z/)
    @local_marks[window.buffer_id][mark] = loc
  else
    @global_marks[mark] = loc
  end
  true
end

#set_option(name, value, scope: :auto, window: current_window, buffer: current_buffer) ⇒ Object



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/ruvim/editor.rb', line 228

def set_option(name, value, scope: :auto, window: current_window, buffer: current_buffer)
  key = name.to_s
  value = coerce_option_value(key, value)
  actual_scope = (scope == :auto ? option_default_scope(key) : scope)
  case actual_scope
  when :global
    @global_options[key] = value
  when :buffer
    raise RuVim::CommandError, "No current buffer" unless buffer
    buffer.options[key] = value
  when :window
    raise RuVim::CommandError, "No current window" unless window
    window.options[key] = value
  else
    raise RuVim::CommandError, "Unknown option scope: #{actual_scope}"
  end
  value
end

#set_quickfix_list(items) ⇒ Object



913
914
915
916
917
# File 'lib/ruvim/editor.rb', line 913

def set_quickfix_list(items)
  ary = Array(items).map { |it| normalize_location(it)&.merge(text: (it[:text] || it["text"]).to_s) }.compact
  @quickfix_list = { items: ary, index: nil }
  @quickfix_list
end

#set_register(name = "\"", text:, type: :charwise) ⇒ Object



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/ruvim/editor.rb', line 281

def set_register(name = "\"", text:, type: :charwise)
  key = name.to_s
  return { text: text, type: type } if key == "_"

  payload = write_register_payload(key, text: text, type: type)
  write_clipboard_register(key, payload)
  if key == "\""
    if (default_clip = clipboard_default_register_key)
      mirror = write_register_payload(default_clip, text: payload[:text], type: payload[:type])
      write_clipboard_register(default_clip, mirror)
    end
  end
  @registers["\""] = payload unless key == "\""
  payload
end

#show_help_buffer!(title:, lines:, filetype: "help") ⇒ Object



773
774
775
776
777
778
779
780
781
782
783
784
785
# File 'lib/ruvim/editor.rb', line 773

def show_help_buffer!(title:, lines:, filetype: "help")
  buffer = add_virtual_buffer(
    kind: :help,
    name: title,
    lines: Array(lines),
    filetype: filetype,
    readonly: true,
    modifiable: false
  )
  switch_to_buffer(buffer.id)
  echo(title)
  buffer
end

#show_intro_buffer_if_applicable!Object



787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
# File 'lib/ruvim/editor.rb', line 787

def show_intro_buffer_if_applicable!
  return unless @buffers.length == 1
  return unless current_buffer.file_buffer?
  return unless current_buffer.path.nil?
  return unless current_buffer.lines == [""]
  return if current_buffer.modified?

  current_buffer.replace_all_lines!(intro_lines)
  current_buffer.configure_special!(kind: :intro, name: "[Intro]", readonly: true, modifiable: false)
  current_buffer.modified = false
  assign_filetype(current_buffer, "help")
  current_window.cursor_x = 0
  current_window.cursor_y = 0
  current_window.row_offset = 0
  current_window.col_offset = 0
  clear_message
  current_buffer
end

#split_current_window(layout: :horizontal, place: :after) ⇒ Object



615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/ruvim/editor.rb', line 615

def split_current_window(layout: :horizontal, place: :after)
  save_current_tabpage_state! unless @suspend_tab_autosave
  src = current_window
  id = next_window_id
  win = Window.new(id:, buffer_id: src.buffer_id)
  @windows[id] = win
  ensure_initial_tabpage!
  win.cursor_x = src.cursor_x
  win.cursor_y = src.cursor_y
  win.row_offset = src.row_offset
  win.col_offset = src.col_offset

  split_type = (layout == :vertical ? :vsplit : :hsplit)
  new_leaf = { type: :window, id: win.id }

  @layout_tree = tree_split_leaf(@layout_tree, src.id, split_type, new_leaf, place)

  @current_window_id = win.id
  save_current_tabpage_state! unless @suspend_tab_autosave
  win
end

#start_macro_recording(name) ⇒ Object



360
361
362
363
364
365
366
# File 'lib/ruvim/editor.rb', line 360

def start_macro_recording(name)
  reg = name.to_s
  return false unless reg.match?(/\A[A-Za-z0-9]\z/)

  @macro_recording = { name: reg, keys: [] }
  true
end

#stop_macro_recordingObject



368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/ruvim/editor.rb', line 368

def stop_macro_recording
  rec = @macro_recording
  @macro_recording = nil
  return nil unless rec

  name = rec[:name]
  keys = rec[:keys]
  if name.match?(/\A[A-Z]\z/)
    base = name.downcase
    @macros[base] = [*(@macros[base] || []), *keys]
    @macros[base]
  else
    @macros[name.downcase] = keys
  end
end

#store_operator_register(name = "\"", text:, type:, kind:) ⇒ Object



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/ruvim/editor.rb', line 297

def store_operator_register(name = "\"", text:, type:, kind:)
  key = (name || "\"")
  payload = { text: text, type: type }
  return payload if key == "_"

  written = set_register(key, text: payload[:text], type: payload[:type])
  op_payload = dup_register_payload(written)

  case kind
  when :yank
    @registers["0"] = dup_register_payload(op_payload)
  when :delete, :change
    rotate_delete_registers!(op_payload)
  end

  written
end

#stream_stop_or_cancel!Object



180
181
182
183
184
185
# File 'lib/ruvim/editor.rb', line 180

def stream_stop_or_cancel!
  handler = current_buffer&.stream&.stop_handler
  return false unless handler

  handler.call
end

#suppress_hlsearch!Object



160
161
162
# File 'lib/ruvim/editor.rb', line 160

def suppress_hlsearch!
  @hlsearch_suppressed = true
end

#switch_to_buffer(buffer_id) ⇒ Object



737
738
739
740
741
742
743
744
745
746
747
748
749
# File 'lib/ruvim/editor.rb', line 737

def switch_to_buffer(buffer_id)
  prev_buffer_id = current_window&.buffer_id
  current_window.buffer_id = buffer_id
  current_window.cursor_x = 0
  current_window.cursor_y = 0
  current_window.row_offset = 0
  current_window.col_offset = 0
  if prev_buffer_id && prev_buffer_id != buffer_id
    @alternate_buffer_id = prev_buffer_id
  end
  save_current_tabpage_state! unless @suspend_tab_autosave
  current_window
end

#tabnew(path: nil) ⇒ Object



1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
# File 'lib/ruvim/editor.rb', line 1011

def tabnew(path: nil)
  ensure_initial_tabpage!
  save_current_tabpage_state!

  with_tab_autosave_suspended do
    @layout_tree = nil
    @current_window_id = nil

    buffer = path ? add_buffer_from_file(path) : add_empty_buffer
    add_window(buffer_id: buffer.id)
    tab = new_tabpage_snapshot
    @tabpages << tab
    @current_tabpage_index = @tabpages.length - 1
    load_tabpage_state!(tab)
    return tab
  end
end

#tabnext(step = 1) ⇒ Object



1029
1030
1031
1032
1033
1034
# File 'lib/ruvim/editor.rb', line 1029

def tabnext(step = 1)
  return nil if @tabpages.empty?
  save_current_tabpage_state!
  @current_tabpage_index = (@current_tabpage_index + step) % @tabpages.length
  load_tabpage_state!(@tabpages[@current_tabpage_index])
end

#tabpage_countObject



893
894
895
# File 'lib/ruvim/editor.rb', line 893

def tabpage_count
  @tabpages.length
end

#tabpage_windows(tab) ⇒ Object



897
898
899
# File 'lib/ruvim/editor.rb', line 897

def tabpage_windows(tab)
  tree_leaves(tab[:layout_tree])
end

#tabpagesObject



881
882
883
# File 'lib/ruvim/editor.rb', line 881

def tabpages
  @tabpages
end

#tabprev(step = 1) ⇒ Object



1036
1037
1038
# File 'lib/ruvim/editor.rb', line 1036

def tabprev(step = 1)
  tabnext(-step)
end

#text_viewport_size(rows:, cols:) ⇒ Object



1128
1129
1130
1131
1132
# File 'lib/ruvim/editor.rb', line 1128

def text_viewport_size(rows:, cols:)
  # Reserve one status row + one command/error row at the bottom.
  text_rows = rows - 2
  [text_rows, cols]
end

#transient_message_timeout_seconds(now: nil) ⇒ Object



1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
# File 'lib/ruvim/editor.rb', line 1103

def transient_message_timeout_seconds(now: nil)
  return nil unless @message_deadline
  return nil if message_error?
  return nil if command_line_active?

  now ||= Process.clock_gettime(Process::CLOCK_MONOTONIC)
  [@message_deadline - now, 0.0].max
rescue StandardError
  nil
end

#visual_active?Boolean

Returns:

  • (Boolean)


462
463
464
# File 'lib/ruvim/editor.rb', line 462

def visual_active?
  !@visual_state.nil?
end

#visual_selection(window = current_window) ⇒ Object



480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
# File 'lib/ruvim/editor.rb', line 480

def visual_selection(window = current_window)
  return nil unless @visual_state

  ay = @visual_state[:anchor_y]
  ax = @visual_state[:anchor_x]
  cy = window.cursor_y
  cx = window.cursor_x
  case @visual_state[:mode]
  when :visual_line
    start_row, end_row = [ay, cy].minmax
    {
      mode: :linewise,
      start_row: start_row,
      start_col: 0,
      end_row: end_row,
      end_col: current_buffer.line_length(end_row)
    }
  when :visual_block
    start_row, end_row = [ay, cy].minmax
    start_col, end_col = [ax, cx].minmax
    {
      mode: :blockwise,
      start_row: start_row,
      start_col: start_col,
      end_row: end_row,
      end_col: end_col + 1
    }
  else
    if ay < cy || (ay == cy && ax <= cx)
      s_row, s_col, e_row, e_col = [ay, ax, cy, cx]
    else
      s_row, s_col, e_row, e_col = [cy, cx, ay, ax]
    end
    {
      mode: :charwise,
      start_row: s_row,
      start_col: s_col,
      end_row: e_row,
      end_col: e_col + 1
    }
  end
end

#visual_stateObject



344
345
346
# File 'lib/ruvim/editor.rb', line 344

def visual_state
  @visual_state
end

#window_countObject



901
902
903
# File 'lib/ruvim/editor.rb', line 901

def window_count
  tree_leaves(@layout_tree).length
end

#window_layoutObject



872
873
874
875
876
877
878
879
# File 'lib/ruvim/editor.rb', line 872

def window_layout
  return :single if @layout_tree.nil? || @layout_tree[:type] == :window
  case @layout_tree[:type]
  when :vsplit then :vertical
  when :hsplit then :horizontal
  else :single
  end
end

#window_orderObject



868
869
870
# File 'lib/ruvim/editor.rb', line 868

def window_order
  tree_leaves(@layout_tree)
end