Class: Canopus::Vim

Inherits:
Object
  • Object
show all
Includes:
Commandable, Motionable, OperatorCapable, TextObjectSelectable
Defined in:
lib/canopus/vim.rb,
sig/vim.rbs

Defined Under Namespace

Modules: Commandable, Motionable, OperatorCapable, TextObjectSelectable

Constant Summary collapse

OPERATORS =

Returns:

  • (Array[String])
%w[d c y > < = gu gU g~].freeze
VISUAL_MODES =

Returns:

  • (Array[Symbol])
%i[visual visual_line visual_block].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(editor) ⇒ Vim

Returns a new instance of Vim.

Parameters:



10
11
12
13
14
15
# File 'lib/canopus/vim.rb', line 10

def initialize(editor)
  @editor, @mode = editor, :normal
  @registers, @marks, @macros = {}, {}, {}
  @count, @command, @last_change = +"", [], []
  @register, @status, @play_depth = '"', "NORMAL", 0
end

Instance Attribute Details

#command_line ⇒ String? (readonly)

Returns the value of attribute command_line.

Returns:

  • (String, nil)


7
8
9
# File 'lib/canopus/vim.rb', line 7

def command_line
  @command_line
end

#macros ⇒ Hash[String, Array[String]] (readonly)

Returns the value of attribute macros.

Returns:

  • (Hash[String, Array[String]])


7
8
9
# File 'lib/canopus/vim.rb', line 7

def macros
  @macros
end

#marks ⇒ Hash[String, Buffer::Anchor] (readonly)

Returns the value of attribute marks.

Returns:



7
8
9
# File 'lib/canopus/vim.rb', line 7

def marks
  @marks
end

#mode ⇒ mode (readonly)

Returns the value of attribute mode.

Returns:



7
8
9
# File 'lib/canopus/vim.rb', line 7

def mode
  @mode
end

#on_command ⇒ ^(String) -> void?

Returns the value of attribute on_command.

Returns:

  • (^(String) -> void, nil)


8
9
10
# File 'lib/canopus/vim.rb', line 8

def on_command
  @on_command
end

#registers ⇒ Hash[String, register] (readonly)

Returns the value of attribute registers.

Returns:

  • (Hash[String, register])


7
8
9
# File 'lib/canopus/vim.rb', line 7

def registers
  @registers
end

#status ⇒ String (readonly)

Returns the value of attribute status.

Returns:

  • (String)


7
8
9
# File 'lib/canopus/vim.rb', line 7

def status
  @status
end

Instance Method Details

#cursor_position ⇒ Integer

Zero-based UTF-8 byte offset of the active edge, including visual mode.

Returns:

  • (Integer)


21
# File 'sig/vim.rbs', line 21

def cursor_position = visual? ? @visual_head : @editor.primary.head

#deactivate ⇒ self

On blur: finish insert history and return to Normal, preserving registers.

Returns:

  • (self)


21
22
23
24
25
26
# File 'lib/canopus/vim.rb', line 21

def deactivate
  %i[insert replace].include?(@mode) ? insert_key("esc") : escape
  @command_line = nil
  @status = "NORMAL"
  self
end

#dispose ⇒ void

This method returns an undefined value.

On close: finish history and release every owned buffer anchor.



25
26
27
28
29
30
# File 'sig/vim.rbs', line 25

def dispose
  end_group
  (@marks.values + [@block_insert_start]).compact.uniq.each { |anchor| @editor.buffer.release_anchor(anchor) }
  @marks.clear
  @block_insert_start = nil
end

#feed(key) ⇒ void

This method returns an undefined value.

Input is a single grapheme or a normalized key such as "esc" or "ctrl-v".

Parameters:

  • key (String)


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'sig/vim.rbs', line 19

def feed(key)
  @message = nil
  key = "esc" if key == "\e"
  key = "enter" if ["\r", "\n"].include?(key)
  @macros[@recording] << key if @recording && !(key == "q" && @mode == :normal && !@prefix)
  return command_key(key) if @command_line
  return insert_key(key) if %i[insert replace].include?(@mode)
  @visual_keys << key if visual? && @visual_keys
  @command << key
  return escape if %w[esc ctrl-c].include?(key)
  return prefix_key(key) if @prefix
  if key.match?(/\A\d\z/) && (key != "0" || !@count.empty?)
    @count << key
    return
  end
  explicit = !@count.empty?
  count = explicit ? @count.to_i.clamp(1, 10_000) : 1
  @count.clear
  return operate_key(key, count, explicit) if @operator
  if visual? && (%w[d c y > < = x s D C Y].include?(key))
    return operate_visual({"x" => "d", "s" => "c", "D" => "d", "C" => "c", "Y" => "y"}.fetch(key, key), count: count)
  end
  if visual? && %w[i a].include?(key)
    @prefix, @prefix_count = key, count
    return
  end
  case key
  when "i", "a", "I", "A", "o", "O", "R"
    enter_insert(key, count)
  when "v", "V", "ctrl-v"
    requested = {"v" => :visual, "V" => :visual_line, "ctrl-v" => :visual_block}.fetch(key)
    if @mode == requested
      escape
    else
      @visual_anchor = @visual_head = cursor_position unless visual?
      @mode = requested
      @visual_keys = @command.dup
      update_visual
      @command.clear
    end
  when "d", "c", "y", ">", "<", "="
    @operator, @operator_count, @operator_explicit = key, count, explicit
  when "g", "f", "F", "t", "T", "m", "'", "`", '"', "@", "r"
    @prefix, @prefix_count, @prefix_explicit = key, count, explicit
  when "q"
    if @recording
      save_macro_register(@recording)
      @recording = nil
      @command.clear
    else
      @prefix = "q"
    end
  when "x", "X", "s"
    first = cursor_position
    endpoint = horizontal(first, key == "X" ? -count : count, insertion: true)
    operate(key == "s" ? "c" : "d", [first, endpoint].min...[first, endpoint].max)
  when "D", "C", "Y", "S"
    if %w[Y S].include?(key)
      operate(key == "Y" ? "y" : "c", line_range(count), linewise: true)
    else
      ending = line_end([row_at(cursor_position) + count - 1, last_row].min)
      operate(key == "D" ? "d" : "c", cursor_position...ending)
    end
  when "p", "P" then paste(key == "p", count)
  when "u", "ctrl-r"
    groups = @macro_groups || 0
    groups.times { @editor.buffer.end_undo_group }
    begin
      count.times { key == "u" ? @editor.undo : @editor.redo }
    ensure
      groups.times { @editor.buffer.begin_undo_group }
    end
    @editor.select(normal_offset(@editor.primary.head))
    @command.clear
  when "."
    change = @last_change.dup
    @command.clear
    count.times { replay(change) }
  when "J" then join_lines(count)
  when "~"
    if visual?
      operate_visual("g~")
    else
      first = cursor_position
      ending = horizontal(first, count, insertion: true)
      operate("g~", first...ending, destination: normal_offset(ending))
    end
  when ":", "/", "?"
    @command_line, @command_type = +"", key
    @command_line = +"'<,'>" if key == ":" && visual?
  when "n", "N"
    search_next(reverse: key == "N", count: count)
    @command.clear
  when "*", "#"
    range = text_object("w", inner: true, count: 1)
    if range
      @search = "\\b#{Regexp.escape(@editor.buffer.rope.byteslice(range).to_s)}\\b"
      @search_direction = key == "*" ? 1 : -1
      search_next(count: count)
    end
    @command.clear
  else
    move(key, count, explicit: explicit)
    @command.clear
  end
rescue Error
  end_group
  @mode, @operator, @prefix = :normal, nil, nil
  raise
ensure
  @status = @message || (@command_line ? "#{@command_type}#{@command_line}" : @mode.to_s.upcase)
end