Module: Textbringer::Commands

Includes:
Utils
Included in:
GlobalMinorMode, GlobalMinorMode, InputMethod, InputMethod, MinorMode, MinorMode, Mode, Mode
Defined in:
lib/textbringer/commands.rb,
lib/textbringer/commands/lsp.rb,
lib/textbringer/commands/fill.rb,
lib/textbringer/commands/help.rb,
lib/textbringer/commands/misc.rb,
lib/textbringer/commands/ctags.rb,
lib/textbringer/commands/files.rb,
lib/textbringer/commands/ispell.rb,
lib/textbringer/commands/server.rb,
lib/textbringer/commands/buffers.rb,
lib/textbringer/commands/dabbrev.rb,
lib/textbringer/commands/isearch.rb,
lib/textbringer/commands/replace.rb,
lib/textbringer/commands/windows.rb,
lib/textbringer/commands/register.rb,
lib/textbringer/commands/clipboard.rb,
lib/textbringer/commands/rectangle.rb,
lib/textbringer/commands/completion.rb,
lib/textbringer/commands/input_method.rb,
lib/textbringer/commands/ucs_normalize.rb,
lib/textbringer/commands/keyboard_macro.rb,
lib/textbringer/input_methods/skk_input_method.rb

Defined Under Namespace

Modules: SymbolDump Classes: BufferPosition, Ispell

Constant Summary collapse

LSP_DOCUMENT_VERSIONS =
{}
LSP_STATUS =
{
  signature_window: nil
}
HELP_RING =
Ring.new
CTAGS =
{
  path: nil,
  tags: nil,
  tags_mtime: nil,
  name: nil,
  candidates: nil,
  index: nil,
  tag_mark_stack: []
}
ISPELL_STATUS =
{}
URI_REGEXP =
URI::RFC2396_PARSER.make_regexp(["http", "https", "ftp", "mailto"])
EMAIL_REGEXP =
/
  # local-part
  (  # dot-atom
     (?<atom>[0-9a-z!\#$%&'*+\-\/=?^_`{|}~]+)
     (\.\g<atom>)*
  |  # quoted-string
    \"([\x20\x21\x23-\x5b\x5d-\x7e]
       |\\[\x20-\x7e])*\"
  )@
  # domain
  (?<sub_domain>[0-9a-z]([0-9a-z-]*[0-9a-z])?)
  (\.\g<sub_domain>)*
/ix
ISPELL_WORD_REGEXP =
/
    (?<uri>#{URI_REGEXP})
  | (?<email>#{EMAIL_REGEXP})
  | (?<word>[[:alpha:]]+(?:'[[:alpha:]]+)*)
/x
ISEARCH_STATUS =
{
  forward: true,
  string: "",
  last_string: "",
  start: 0,
  last_pos: 0,
  recursive_edit: false
}
RE_SEARCH_STATUS =
{
  last_regexp: nil
}
REGISTERS =
{}
CLIPBOARD_AVAILABLE =
Clipboard.implementation.name != "Clipboard::File"
COMPLETION_POPUP_STATUS =
{
  active: false,
  start_point: nil
}
KEYBOARD_MACROS =
{}

Constants included from Utils

Utils::COMPLETION, Utils::EXPRESSION_COMPLETOR, Utils::EXPRESSION_COMPLETOR_OPTIONS, Utils::HOOKS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Utils

add_hook, background, complete_for_minibuffer, delete_completions_window, foreground, foreground!, get_hooks, message, read_buffer, read_char, read_command_name, read_encoding, read_event, read_expression, read_file_name, read_from_minibuffer, read_key_sequence, read_object, read_single_char, received_keyboard_quit?, remove_hook, ruby_install_name, run_hooks, run_hooks_in, self_insert_and_exit_minibuffer, set_transient_map, show_exception, sit_for, sleep_for, y_or_n?, yes_or_no?

Class Method Details

.[](name) ⇒ Object



24
25
26
# File 'lib/textbringer/commands.rb', line 24

def self.[](name)
  @command_table[name.intern]
end

.command_tableObject



20
21
22
# File 'lib/textbringer/commands.rb', line 20

def self.command_table
  @command_table
end

.completion_popup_mode_active?Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/textbringer/commands/completion.rb', line 28

def self.completion_popup_mode_active?
  COMPLETION_POPUP_STATUS[:active]
end

.define_command(name, doc: "No documentation", source_location_proc: nil, &block) ⇒ Object



28
29
30
31
32
33
34
# File 'lib/textbringer/commands.rb', line 28

def define_command(name, doc: "No documentation", source_location_proc: nil, &block)
  name = name.intern
  Commands.send(:define_method, name, &block)
  Commands.send(:module_function, name)
  Commands.command_table[name] = Command.new(name, block, doc, source_location_proc)
  name
end

.listObject



16
17
18
# File 'lib/textbringer/commands.rb', line 16

def self.list
  @command_table.keys
end

.undefine_command(name) ⇒ Object



37
38
39
40
41
42
43
# File 'lib/textbringer/commands.rb', line 37

def undefine_command(name)
  name = name.intern
  if Commands.command_table.key?(name)
    Commands.send(:undef_method, name)
    Commands.command_table.delete(name)
  end
end

Instance Method Details

#buffer_uri(buffer) ⇒ Object



227
228
229
230
231
232
233
# File 'lib/textbringer/commands/lsp.rb', line 227

def buffer_uri(buffer)
  if buffer.file_name
    "file://#{buffer.file_name}"
  else
    "untitled:#{buffer.name}"
  end
end

#command_help(cmd) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/textbringer/commands/help.rb', line 67

def command_help(cmd)
  file, line = *cmd.source_location
  s = format("%s:%d\n", file, line)
  s << "-" * (Window.columns - 2) + "\n"
  s << "#{cmd.name}"
  if !cmd.block.parameters.empty?
    s << "("
    s << cmd.block.parameters.map { |_, param| param }.join(", ")
    s << ")"
  end
  s << "\n\n"
  s << "-" * (Window.columns - 2) + "\n\n"
  s << cmd.doc
  s << "\n"
  s
end

#completion_popup_doneObject



89
90
91
92
93
94
# File 'lib/textbringer/commands/completion.rb', line 89

def completion_popup_done
  COMPLETION_POPUP_STATUS[:active] = false
  COMPLETION_POPUP_STATUS[:start_point] = nil
  Controller.current.overriding_map = nil
  remove_hook(:pre_command_hook, :completion_popup_pre_command_hook)
end

#completion_popup_pre_command_hookObject



96
97
98
99
100
101
102
# File 'lib/textbringer/commands/completion.rb', line 96

def completion_popup_pre_command_hook
  # Close popup if command is not a completion popup command
  if /\Acompletion_popup_/ !~ Controller.current.this_command.to_s
    CompletionPopup.instance.close
    completion_popup_done
  end
end

#completion_popup_start(items:, start_point:, prefix: "") ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/textbringer/commands/completion.rb', line 73

def completion_popup_start(items:, start_point:, prefix: "")
  return if items.empty?

  CompletionPopup.instance.show(
    items: items,
    start_point: start_point,
    prefix: prefix
  )

  COMPLETION_POPUP_STATUS[:active] = true
  COMPLETION_POPUP_STATUS[:start_point] = start_point

  Controller.current.overriding_map = COMPLETION_POPUP_MAP
  add_hook(:pre_command_hook, :completion_popup_pre_command_hook)
end

#current_prefix_argObject



167
168
169
# File 'lib/textbringer/commands/misc.rb', line 167

def current_prefix_arg
  Controller.current.current_prefix_arg
end

#ensure_ispell_activeObject



228
229
230
231
232
# File 'lib/textbringer/commands/ispell.rb', line 228

def ensure_ispell_active
  if ISPELL_STATUS[:ispell].nil?
    raise EditorError, "ispell is not active"
  end
end

#execute_keyboard_macro(macro, n = 1) ⇒ Object



38
39
40
# File 'lib/textbringer/commands/keyboard_macro.rb', line 38

def execute_keyboard_macro(macro, n = 1)
  Controller.current.execute_keyboard_macro(macro, n)
end

#get_tagsObject



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/textbringer/commands/ctags.rb', line 73

def get_tags
  path = File.expand_path("tags")
  mtime = File.mtime(path)
  if CTAGS[:path] != path || CTAGS[:tags_mtime] != mtime
    CTAGS[:path] = path
    tags = Hash.new { |h, k| h[k] = [] }
    File.binread(path).scan(/^(.*?)\t(.*?)\t(.*?)(?:;".*)?$/) do
      |name, file, addr|
      n = tags[name].count { |f,| f == file } + 1
      tags[name].push([file, addr, n])
    end
    CTAGS[:tags] = tags
    CTAGS[:tags_mtime] = mtime
    message("Loaded #{path}")
  end
  CTAGS[:tags]
end

#insert_completion(item) ⇒ Object



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
131
# File 'lib/textbringer/commands/completion.rb', line 104

def insert_completion(item)
  buffer = Buffer.current
  start_point = COMPLETION_POPUP_STATUS[:start_point]
  return unless start_point

  insert_text = item[:insert_text] || item[:label]
  return unless insert_text

  # The server may return an insert_text that covers more than the typed
  # symbol prefix (e.g. "Textbringer::Buffer" when the symbol pattern only
  # backed up to "Buf"). Find the longest prefix of insert_text that is a
  # suffix of the buffer text ending at point, and use that as the
  # replacement range.
  look_back = [buffer.point, insert_text.length].min
  actual_start = start_point
  if look_back > 0
    text_before_point = buffer.substring(buffer.point - look_back, buffer.point)
    look_back.downto(1) do |n|
      if text_before_point.end_with?(insert_text[0, n])
        actual_start = buffer.point - n
        break
      end
    end
  end

  buffer.delete_region(actual_start, buffer.point)
  buffer.insert(insert_text)
end

#isearch_doneObject



79
80
81
82
83
84
85
86
87
# File 'lib/textbringer/commands/isearch.rb', line 79

def isearch_done
  Buffer.current.delete_isearch_mark
  Controller.current.overriding_map = nil
  remove_hook(:pre_command_hook, :isearch_pre_command_hook)
  ISEARCH_STATUS[:last_string] = ISEARCH_STATUS[:string]
  if ISEARCH_STATUS[:recursive_edit]
    exit_recursive_edit
  end
end

#isearch_mode(forward, recursive_edit: false) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/textbringer/commands/isearch.rb', line 45

def isearch_mode(forward, recursive_edit: false)
  ISEARCH_STATUS[:forward] = forward
  ISEARCH_STATUS[:string] = +""
  ISEARCH_STATUS[:recursive_edit] = recursive_edit
  Controller.current.overriding_map = ISEARCH_MODE_MAP
  run_hooks(:isearch_mode_hook)
  add_hook(:pre_command_hook, :isearch_pre_command_hook)
  ISEARCH_STATUS[:start] = ISEARCH_STATUS[:last_pos] = Buffer.current.point
  if Buffer.current != Buffer.minibuffer
    message(isearch_prompt, log: false)
  end
  if recursive_edit
    recursive_edit()
  end
end

#isearch_mode?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/textbringer/commands/isearch.rb', line 61

def isearch_mode?
  Controller.current.overriding_map == ISEARCH_MODE_MAP
end

#isearch_pre_command_hookObject



73
74
75
76
77
# File 'lib/textbringer/commands/isearch.rb', line 73

def isearch_pre_command_hook
  if /\Aisearch_/ !~ Controller.current.this_command
    isearch_done
  end
end

#isearch_promptObject



65
66
67
68
69
70
71
# File 'lib/textbringer/commands/isearch.rb', line 65

def isearch_prompt
  if ISEARCH_STATUS[:forward]
    "I-search: "
  else
    "I-search backward: "
  end
end

#isearch_repeat(forward) ⇒ Object



176
177
178
179
180
181
182
183
# File 'lib/textbringer/commands/isearch.rb', line 176

def isearch_repeat(forward)
  ISEARCH_STATUS[:forward] = forward
  ISEARCH_STATUS[:last_pos] = Buffer.current.point
  if ISEARCH_STATUS[:string].empty?
    ISEARCH_STATUS[:string] = ISEARCH_STATUS[:last_string]
  end
  isearch_search
end

#isearch_repeat_backwardObject



172
173
174
# File 'lib/textbringer/commands/isearch.rb', line 172

def isearch_repeat_backward
  isearch_repeat(false)
end

#isearch_repeat_forwardObject



168
169
170
# File 'lib/textbringer/commands/isearch.rb', line 168

def isearch_repeat_forward
  isearch_repeat(true)
end

#isearch_searchObject



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/textbringer/commands/isearch.rb', line 132

def isearch_search
  forward = ISEARCH_STATUS[:forward]
  options = if /\A[A-Z]/.match?(ISEARCH_STATUS[:string])
              nil
            else
              Regexp::IGNORECASE
            end
  re = Regexp.new(Regexp.quote(ISEARCH_STATUS[:string]), options)
  last_pos = ISEARCH_STATUS[:last_pos]
  if forward
    offset = last_pos
  else
    Buffer.current.save_excursion do
      pos = last_pos - ISEARCH_STATUS[:string].bytesize
      goto_char(last_pos)
      while Buffer.current.point > pos
        backward_char
      end
      offset = Buffer.current.point
    end
  end
  if offset >= 0 && Buffer.current.byteindex(forward, re, offset)
    if Buffer.current != Buffer.minibuffer
      message(isearch_prompt + ISEARCH_STATUS[:string], log: false)
    end
    Buffer.current.set_isearch_mark(forward ? match_beginning(0) :
                                    match_end(0))
    goto_char(forward ? match_end(0) : match_beginning(0))
  else
    if Buffer.current != Buffer.minibuffer
      message("Failing " + isearch_prompt + ISEARCH_STATUS[:string],
              log: false)
    end
  end
end

#ispell_doneObject



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/textbringer/commands/ispell.rb', line 112

def ispell_done
  # Don't delete visible_mark if mark is active (transient mark mode)
  unless Buffer.current.mark_active?
    Buffer.current.delete_visible_mark
  end
  Controller.current.overriding_map = nil
  ISPELL_STATUS[:ispell]&.close
  ISPELL_STATUS[:ispell] = nil
  if ISPELL_STATUS[:recursive_edit]
    exit_recursive_edit
  end
  ISPELL_STATUS[:recursive_edit] = false
end

#ispell_forwardObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/textbringer/commands/ispell.rb', line 131

def ispell_forward
  buffer = Buffer.current
  while buffer.re_search_forward(ISPELL_WORD_REGEXP, raise_error: false,
                                 goto_beginning: true)
    if buffer.last_match[:word].nil?
      buffer.goto_char(buffer.match_end(0))
      next
    end
    ispell_beginning = buffer.point
    # Don't update visible_mark if mark is already active (transient mark mode)
    unless buffer.mark_active?
      buffer.set_visible_mark
    end
    buffer.goto_char(buffer.match_end(0))
    word = buffer.match_string(0)
    _original, suggestions = ISPELL_STATUS[:ispell].check_word(word)
    if !suggestions.nil? && !suggestions.empty?
      ISPELL_STATUS[:beginning] = ispell_beginning
      ISPELL_STATUS[:word] = word
      ISPELL_STATUS[:suggestions] = suggestions
      message_misspelled
      recenter
      return false
    end
  end
  Controller.current.overriding_map = nil
  if ISPELL_STATUS[:ispell]&.personal_dictionary_modified? &&
      y_or_n?("Personal dictionary modified.  Save?")
    ISPELL_STATUS[:ispell].save_personal_dictionary
  end
  message("Finished spelling check.")
  ispell_done
  true
end

#ispell_modeObject



126
127
128
129
# File 'lib/textbringer/commands/ispell.rb', line 126

def ispell_mode
  ISPELL_STATUS[:ispell] = Ispell.new
  Controller.current.overriding_map = ISPELL_MODE_MAP
end

#keymap_bindings(keymap) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/textbringer/commands/help.rb', line 32

def keymap_bindings(keymap)
  s = format("%-16s  %s\n", "Key", "Binding")
  s << format("%-16s  %s\n", "---", "-------")
  s << "\n"
  keymap.each do |key_sequence, command|
    if command != :self_insert
      s << format("%-16s  [%s]\n",
                  Keymap.key_sequence_string(key_sequence),
                  command)
    end
  end
  s
end

#lsp_after_set_visited_file_name_hook(old_file_name) ⇒ Object



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/textbringer/commands/lsp.rb', line 370

def lsp_after_set_visited_file_name_hook(old_file_name)
  buffer = Buffer.current

  # Close the old document
  old_uri = old_file_name ? "file://#{old_file_name}" : nil
  return unless old_uri
  client = LSP::ServerRegistry.get_client_for_buffer(buffer)
  if client&.running? && client.document_open?(old_uri)
    client.did_close(uri: old_uri)
    LSP_DOCUMENT_VERSIONS.delete(old_uri)
  end

  # Reset hooks so they are reinstalled with the new URI
  buffer[:lsp_hooks_installed] = false

  # Open the new document
  lsp_open_document(buffer)
end

#lsp_close_signature_windowObject



339
340
341
342
343
344
345
# File 'lib/textbringer/commands/lsp.rb', line 339

def lsp_close_signature_window
  win = LSP_STATUS[:signature_window]
  if win && !win.deleted?
    win.close
  end
  LSP_STATUS[:signature_window] = nil
end

#lsp_completion_context(prefix, trigger_chars, char_before_start) ⇒ Object

Helper methods



185
186
187
188
189
190
191
192
# File 'lib/textbringer/commands/lsp.rb', line 185

def lsp_completion_context(prefix, trigger_chars, char_before_start)
  if prefix.empty? && trigger_chars.include?(char_before_start)
    { triggerKind: 2, # TriggerCharacter
      triggerCharacter: char_before_start }
  else
    { triggerKind: 1 } # Invoked
  end
end

#lsp_find_file_hookObject



361
362
363
# File 'lib/textbringer/commands/lsp.rb', line 361

def lsp_find_file_hook
  lsp_open_document(Buffer.current)
end

#lsp_open_document(buffer) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/textbringer/commands/lsp.rb', line 235

def lsp_open_document(buffer)
  client = LSP::ServerRegistry.get_client_for_buffer(buffer)
  return unless client
  return unless client.running? && client.initialized?

  uri = buffer_uri(buffer)
  return if client.document_open?(uri)

  version = 1
  LSP_DOCUMENT_VERSIONS[uri] = version
  language_id = LSP::ServerRegistry.language_id_for_buffer(buffer) || "text"
  client.did_open(
    uri: uri,
    language_id: language_id,
    version: version,
    text: buffer.to_s
  )
  unless buffer[:lsp_hooks_installed]
    lsp_setup_buffer_hooks(buffer, client, uri)
    buffer[:lsp_hooks_installed] = true
  end
end

#lsp_position(buffer, pos) ⇒ Object

Compute LSP position (0-based line, UTF-16 character offset) from a buffer position.



214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/textbringer/commands/lsp.rb', line 214

def lsp_position(buffer, pos)
  line, = buffer.pos_to_line_and_column(pos)
  # Get the text from the start of the line to compute UTF-16 offset
  line_start = buffer.save_point do
    buffer.goto_char(pos)
    buffer.beginning_of_line
    buffer.point
  end
  text_on_line = buffer.substring(line_start, pos)
  character = lsp_utf16_length(text_on_line)
  { line: line - 1, character: character }
end

#lsp_setup_buffer_hooks(buffer, client, uri) ⇒ Object

Set up buffer hooks for document synchronization



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/textbringer/commands/lsp.rb', line 259

def lsp_setup_buffer_hooks(buffer, client, uri)
  # Track changes and send updates to LSP server.
  # Sync kind is determined by the server's textDocumentSync capability:
  #   1 = Full (send complete document text on every change)
  #   2 = Incremental (send only the changed range)
  add_hook(:after_change_functions, local: true) do |beg_pos, end_pos, old_text|
    next unless client.running? && client.document_open?(uri)

    version = LSP_DOCUMENT_VERSIONS[uri] || 0
    version += 1
    LSP_DOCUMENT_VERSIONS[uri] = version

    sync_kind = lsp_text_document_sync_kind(client.server_capabilities)

    next if sync_kind == 0 # None: server does not want change notifications

    if sync_kind == 1
      # Full document sync (e.g. solargraph). Note: buffer.to_s is called
      # on every change; this is expected behavior for Full-sync servers.
      client.did_change(uri: uri, version: version, text: buffer.to_s)
    else
      # Incremental sync
      # Compute start position in LSP coordinates (0-based, UTF-16)
      start_pos = lsp_position(buffer, beg_pos)

      if old_text.empty?
        # Insertion: old range is empty, new text is the inserted content
        new_text = buffer.substring(beg_pos, end_pos)
        range = { start: start_pos, end: start_pos }
      else
        # Deletion: compute old end position from the deleted text
        newline_count = old_text.count("\n")
        if newline_count == 0
          end_line = start_pos[:line]
          end_char = start_pos[:character] + lsp_utf16_length(old_text)
        else
          end_line = start_pos[:line] + newline_count
          last_newline = old_text.rindex("\n")
          end_char = lsp_utf16_length(old_text[last_newline + 1..])
        end
        range = {
          start: start_pos,
          end: { line: end_line, character: end_char }
        }
        new_text = ""
      end

      client.did_change(
        uri: uri, version: version,
        text: new_text, range: range
      )
    end
  end

  # Close document when buffer is killed
  buffer.on_killed do
    if client.running?
      client.did_close(uri: uri)
      LSP_DOCUMENT_VERSIONS.delete(uri)
    end
  end
end

#lsp_show_signature_window(label) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/textbringer/commands/lsp.rb', line 322

def lsp_show_signature_window(label)
  # Close any existing signature window
  lsp_close_signature_window

  columns = [[Buffer.display_width(label) + 2, Curses.cols - 2].min, 1].max
  win = FloatingWindow.at_cursor(
    lines: 1,
    columns: columns
  )
  win.buffer.insert(label)
  win.buffer.beginning_of_buffer
  win.show
  LSP_STATUS[:signature_window] = win

  add_hook(:pre_command_hook, :lsp_signature_pre_command_hook)
end

#lsp_signature_pre_command_hookObject



347
348
349
350
# File 'lib/textbringer/commands/lsp.rb', line 347

def lsp_signature_pre_command_hook
  lsp_close_signature_window
  remove_hook(:pre_command_hook, :lsp_signature_pre_command_hook)
end

#lsp_text_document_sync_kind(server_capabilities) ⇒ Object

Resolve textDocumentSync to a TextDocumentSyncKind integer. The server capability may be an Integer or a TextDocumentSyncOptions Hash. Returns 0 (None), 1 (Full), or 2 (Incremental). Defaults to 2.



197
198
199
200
201
202
203
204
# File 'lib/textbringer/commands/lsp.rb', line 197

def lsp_text_document_sync_kind(server_capabilities)
  sync = server_capabilities["textDocumentSync"]
  case sync
  when Integer then sync
  when Hash then sync["change"]&.to_i || 2
  else 2
  end
end

#lsp_utf16_length(str) ⇒ Object

Convert a string’s character length to UTF-16 code unit count. LSP positions use UTF-16 offsets by default.



208
209
210
# File 'lib/textbringer/commands/lsp.rb', line 208

def lsp_utf16_length(str)
  str.encode("UTF-16LE").bytesize / 2
end

#match_beginning(n) ⇒ Object



21
22
23
# File 'lib/textbringer/commands/replace.rb', line 21

def match_beginning(n)
  Buffer.current.match_beginning(n)
end

#match_end(n) ⇒ Object



25
26
27
# File 'lib/textbringer/commands/replace.rb', line 25

def match_end(n)
  Buffer.current.match_end(n)
end

#match_string(n) ⇒ Object



29
30
31
# File 'lib/textbringer/commands/replace.rb', line 29

def match_string(n)
  Buffer.current.match_string(n)
end

#message_misspelledObject



223
224
225
226
# File 'lib/textbringer/commands/ispell.rb', line 223

def message_misspelled
  word = ISPELL_STATUS[:word]
  message("Misspelled: #{word}  [r]eplace, [a]ccept, [i]nsert, [SPC] to skip, [q]uit")
end

#number_prefix_argObject



184
185
186
# File 'lib/textbringer/commands/misc.rb', line 184

def number_prefix_arg
  prefix_numeric_value(current_prefix_arg)
end

#prefix_numeric_value(arg) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/textbringer/commands/misc.rb', line 171

def prefix_numeric_value(arg)
  case arg
  when Integer
    arg
  when Array
    arg.first
  when :-
    -1
  else
    1
  end
end

#read_input_method_name(prompt, default: CONFIG[:default_input_method]) ⇒ Object



11
12
13
14
15
16
# File 'lib/textbringer/commands/input_method.rb', line 11

def read_input_method_name(prompt, default: CONFIG[:default_input_method])
  f = ->(s) {
    complete_for_minibuffer(s.tr("-", "_"), InputMethod.list)
  }
  read_from_minibuffer(prompt, completion_proc: f, default: default)
end

#read_keyboard_macro(prompt) ⇒ Object



54
55
56
57
58
# File 'lib/textbringer/commands/keyboard_macro.rb', line 54

def read_keyboard_macro(prompt)
  macros = KEYBOARD_MACROS.keys.map(&:to_s)
  f = ->(s) { complete_for_minibuffer(s, macros) }
  read_from_minibuffer(prompt, completion_proc: f)
end

#read_register(prompt) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/textbringer/commands/register.rb', line 26

def read_register(prompt)
  Window.echo_area.show(prompt)
  Window.redisplay
  begin
    register = read_char
    register
  ensure
    Window.echo_area.clear
    Window.redisplay
  end
end

#replace_match(s) ⇒ Object



33
34
35
# File 'lib/textbringer/commands/replace.rb', line 33

def replace_match(s)
  Buffer.current.replace_match(s)
end

#universal_argument_modeObject



158
159
160
# File 'lib/textbringer/commands/misc.rb', line 158

def universal_argument_mode
  set_transient_map(UNIVERSAL_ARGUMENT_MAP)
end