Class: VER::Buffer

Inherits:
Text
  • Object
show all
Extended by:
Forwardable
Includes:
Keymapped
Defined in:
lib/ver/methods/save.rb,
lib/ver/buffer.rb,
lib/ver/buffer/frame.rb,
lib/ver/methods/move.rb,
lib/ver/methods/basic.rb,
lib/ver/buffer/console.rb,
lib/ver/methods/delete.rb,
lib/ver/buffer/matching_brace.rb,
lib/ver/buffer/markup_underline_link.rb,
lib/ver/buffer/invalid_trailing_whitespace.rb

Overview

Some strategies are discussed at:

bitworking.org/news/390/text-editor-saving-routines

I try another, “wasteful” approach, copying the original file to a temporary location, overwriting the contents in the copy, then moving the file to the location of the original file.

This way all permissions should be kept identical without any effort, but it will take up additional disk space.

If there is some failure during the normal saving procedure, we will simply overwrite the original file in place, make sure you have good insurance ;)

TODO: we must write backup files, VER can corrupt files on a system

crash for some reason.

Defined Under Namespace

Classes: Console, Frame, HighlightPending, InvalidTrailingWhitespace, MarkupUnderlineLink, MatchingBrace, Terminal

Constant Summary collapse

OPTIONS =
{
  autoseparators:      false,
  background:          '#000',
  blockcursor:         false,
  borderwidth:         0,
  exportselection:     true,   # provide selection to X automatically
  foreground:          '#fff', # when first showing, it's not highlighted...
  highlightbackground: '#000', # this specifies the colors
  highlightcolor:      '#fff', # that are used when the widget
  highlightthickness:  1,      # has input focus.
  insertbackground:    '#fff',
  relief:              :solid,
  setgrid:             false,
  tabstyle:            :wordprocessor,
  takefocus:           true,
  undo:                false,
  wrap:                :word
}
MODE_STYLES =
{
  :control  => {blockcursor: false},
  :insert   => {blockcursor: false, insertbackground: 'red'},
  /select/  => {blockcursor: true,  insertbackground: 'yellow'},
  /replace/ => {blockcursor: true,  insertbackground: 'orange'},
}
TAG_ALL_MATCHING_OPTIONS =
{ from: '1.0', to: 'end - 1 chars' }
PROJECT_DIRECTORY_GLOB =
'{.git/,.hg/,_darcs/,_FOSSIL_}'
MODELINES =
{
  /\s+(?:ver|vim?|ex):\s*.*$/ => /\s+(?:ver|vim?|ex):\s*(.*)$/,
  /\s+(?:ver|vim?|ex):[^:]+:/ => /\s+(?:ver|vim?|ex):([^:]+):/,
  /^(?:ver|vim?):[^:]+:/      => /^(?:ver|vim?):([^:]+):/,
}
REPEAT_BREAK_CMD =
[
  :repeat_action,
  :undo,
  :redo,
]
REPEAT_BREAK_MODE =
[
  :move,
  :search,
]
None =
Object.new

Instance Attribute Summary collapse

Attributes included from Keymapped

#major_mode

Attributes inherited from Text

#lock

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Keymapped

#minor_mode, #minor_mode?

Methods inherited from Text

#execute, #execute_only, #focus, #index, #inspect, #mark, #mark_next, #mark_previous, #marks, #place_forget, #range, #tag, #tag_ranges, #tags

Constructor Details

#initialize(parent = VER.layout, given_options = {}) ⇒ Buffer

Returns a new instance of Buffer.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ver/buffer.rb', line 106

def initialize(parent = VER.layout, given_options = {})
  @layout = parent
  @frame = Frame.new(parent, self)

  font, tabstop = VER.options.font, VER.options.tabstop
  tabs = font.measure('0') * tabstop

  default_options = OPTIONS.merge(
    font:            font,
    insertofftime:   VER.options.insertofftime,
    insertontime:    VER.options.insertontime,
    tabs:            tabs
  )

  super(frame, default_options.merge(given_options))

  setup
end

Instance Attribute Details

#at_currentObject (readonly)

Returns the value of attribute at_current.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def at_current
  @at_current
end

#at_endObject (readonly)

Returns the value of attribute at_end.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def at_end
  @at_end
end

#at_insertObject (readonly)

Returns the value of attribute at_insert.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def at_insert
  @at_insert
end

#at_selObject

Returns the value of attribute at_sel.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def at_sel
  @at_sel
end

#encodingObject

Returns the value of attribute encoding.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def encoding
  @encoding
end

#filenameObject

Returns the value of attribute filename.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def filename
  @filename
end

#frameObject (readonly)

Returns the value of attribute frame.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def frame
  @frame
end

#layoutObject (readonly)

Returns the value of attribute layout.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def layout
  @layout
end

#lockedObject Also known as: locked?

Returns the value of attribute locked.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def locked
  @locked
end

#matching_braceObject (readonly)

Returns the value of attribute matching_brace.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def matching_brace
  @matching_brace
end

#minibufObject (readonly)

Returns the value of attribute minibuf.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def minibuf
  @minibuf
end

#optionsObject (readonly)

Returns the value of attribute options.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def options
  @options
end

#prefix_argObject

Returns the value of attribute prefix_arg.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def prefix_arg
  @prefix_arg
end

#pristineObject Also known as: pristine?

Returns the value of attribute pristine.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def pristine
  @pristine
end

#project_repoObject

Returns the value of attribute project_repo.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def project_repo
  @project_repo
end

#project_rootObject

Returns the value of attribute project_root.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def project_root
  @project_root
end

#readonlyObject

Returns the value of attribute readonly.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def readonly
  @readonly
end

#registerObject

Returns the value of attribute register.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def register
  @register
end

#skip_prefix_count_onceObject

Returns the value of attribute skip_prefix_count_once.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def skip_prefix_count_once
  @skip_prefix_count_once
end

#snippetsObject (readonly)

Returns the value of attribute snippets.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def snippets
  @snippets
end

#statusObject (readonly)

Returns the value of attribute status.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def status
  @status
end

#symbolicObject Also known as: symbolic?

Returns the value of attribute symbolic.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def symbolic
  @symbolic
end

#syntaxObject

Returns the value of attribute syntax.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def syntax
  @syntax
end

#theme_configObject (readonly)

Returns the value of attribute theme_config.



92
93
94
# File 'lib/ver/buffer.rb', line 92

def theme_config
  @theme_config
end

#undoerObject

Returns the value of attribute undoer.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def undoer
  @undoer
end

#uriObject

Returns the value of attribute uri.



95
96
97
# File 'lib/ver/buffer.rb', line 95

def uri
  @uri
end

Class Method Details

.[](uri, line = nil, char = nil) ⇒ Object



60
61
62
# File 'lib/ver/buffer.rb', line 60

def self.[](uri, line = nil, char = nil)
  find_or_create(uri, line, char)
end

.create(path = nil, line = nil, char = nil) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/ver/buffer.rb', line 64

def self.create(path = nil, line = nil, char = nil)
  VER.layout.create_buffer do |buffer|
    buffer.open(path, line, char)
    yield(buffer) if block_given?
    buffer
  end
end

.find_or_create(uri, line = nil, char = nil, &block) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/ver/buffer.rb', line 72

def self.find_or_create(uri, line = nil, char = nil, &block)
  case uri
  when Pathname, Symbol
    if buffer = VER.buffers.find{|buf| buf.uri == uri }
      buffer.show
      buffer.focus
      buffer.at_insert.go_line_char(line, char)
      yield buffer if block_given?
      buffer
    else
      create(uri, line, char, &block)
    end
  when String
    path = Pathname(uri.to_str).expand_path
    find_or_create(path, line, char, &block)
  else
    raise ArgumentError, "Invalid path: %p" % [path]
  end
end

Instance Method Details

#actionsObject



304
305
306
# File 'lib/ver/buffer.rb', line 304

def actions
  major_mode.action_history
end

#after_open(syntax = nil, line = nil, char = nil) ⇒ Object



477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'lib/ver/buffer.rb', line 477

def after_open(syntax = nil, line = nil, char = nil)
  @undoer = VER::Undo::Tree.new(self)
  VER.opened_file(self)
  layout.wm_title = uri.to_s

  info = load_info

  if line || char
    self.insert = "#{line || 1}.#{char || 0}"
  else
    self.insert = info['insert'] || '1.0'
  end

  VER.buffers << self
  message "Opened #{uri}"

  # Don't rely on the <Map> event, since it's prone to race-conditions.
  finalize_open(syntax || info['syntax'])
rescue => ex
  VER.error(ex)
end

#apply_modelineObject



607
608
609
610
611
612
613
614
615
616
617
618
619
620
# File 'lib/ver/buffer.rb', line 607

def apply_modeline
  MODELINES.each do |search_pattern, extract_pattern|
    found = search(search_pattern, 1.0, :end, :count)
    next if found.empty?

    pos, count = found
    line = get(pos, "#{pos} + #{count} chars")

    line =~ extract_pattern
    $1.scan(/[^:\s]+/) do |option|
      apply_modeline_option(option)
    end
  end
end

#apply_modeline_option(option) ⇒ Object



622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
# File 'lib/ver/buffer.rb', line 622

def apply_modeline_option(option)
  negative = option.gsub!(/^no/, '')
  boolean = !negative

  case option
  when 'ai', 'autoindent'
    set :autoindent, boolean
  when 'et', 'expandtab'
    set :expandtab, boolean
  when /(?:tw|textwidth)=(\d+)/
    set :textwidth, $1.to_i
  when /(?:ts|tabstop)=(\d+)/
    set :tabstop, $1.to_i
  when /(?:sw|shiftwidth)=(\d+)/
    set :shiftwidth, $1.to_i
  when /(?:ft|filetype)=(\w+)/
    set :filetype, $1
  else
    VER.warn "Unknown modeline: %p" % [option]
  end
end

#apply_preferencesObject



984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
# File 'lib/ver/buffer.rb', line 984

def apply_preferences
  return unless @preferences

  @preferences.each do |preference|
    next unless preference[:name] == "Comments"
    if settings = preference[:settings]
      if shell_variables = settings[:shellVariables]
        shell_variables.each do |variable|
          name, value = variable.values_at(:name, :value)
          ENV[name] = value

          case name
          when 'TM_COMMENT_START'
            options.comment_line = value
          end
        end
      end
    end
  end
end

#ask(prompt, options = {}, &action) ⇒ Object



1034
1035
1036
1037
# File 'lib/ver/buffer.rb', line 1034

def ask(prompt, options = {}, &action)
  options[:caller] ||= self
  @minibuf.ask(prompt, options, &action)
end

#ask_go_lineObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ver/methods/move.rb', line 20

def ask_go_line
  initial = $1 if events.last[:unicode] =~ /^(\d+)$/

  question = 'Go to [line number|+lines][,column number]: '
  ask(question, value: initial) do |answer, action|
    case action
    when :attempt
      parse_go_line(answer) do |index|
        self.insert = index
        :abort
      end
    when :modified
      parse_go_line(answer) do |index|
        tag_configure(Methods::Search::TAG, Methods::Search::HIGHLIGHT)
        tag_add(Methods::Search::TAG, index, index.lineend)
        see(index)
        Tk::After.ms(3000){
          tag_remove(Methods::Search::TAG, index, index.lineend)
          at_insert.see
        }
      end
    end
  end
end

#backward_scroll(count = buffer.prefix_count) ⇒ Object



14
15
16
17
18
# File 'lib/ver/methods/move.rb', line 14

def backward_scroll(count = buffer.prefix_count)
  count_abs = count.abs
  yview_scroll(-count_abs, :units)
  prev_line(count_abs)
end

#change_registerObject



711
712
713
714
715
716
717
718
719
720
721
722
723
# File 'lib/ver/buffer.rb', line 711

def change_register
  major_mode.read(1) do |name|
    if unicode = name.unicode
      if unicode.empty?
        warn "invalid name for register: %p" % [name]
      else
        self.register = Register[unicode]
      end
    else
      warn "invalid name for register: %p" % [name]
    end
  end
end

#closeObject



675
676
677
678
679
680
# File 'lib/ver/buffer.rb', line 675

def close
  may_close do
    persist_info
    layout.destroy
  end
end

#default_theme_config=(config) ⇒ Object



1029
1030
1031
1032
# File 'lib/ver/buffer.rb', line 1029

def default_theme_config=(config)
  @theme_config = config
  sync_mode_style
end

#delete(*indices) ⇒ Object



366
367
368
369
370
371
372
# File 'lib/ver/buffer.rb', line 366

def delete(*indices)
  return super unless undoer

  undo_record do |record|
    record.delete(*indices)
  end
end

#delete_trailing_whitespaceObject

Tag and delete all trailing whitespace in the VER::Buffer.



4
5
6
7
8
# File 'lib/ver/methods/delete.rb', line 4

def delete_trailing_whitespace
  @invalid_trailing_whitespace.each_range do |range|
    range.delete
  end
end

#detect_project_pathsObject



592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/ver/buffer.rb', line 592

def detect_project_paths
  return unless filename
  parent = filename.expand_path.dirname

  begin
    (parent/PROJECT_DIRECTORY_GLOB).glob do |repo|
      self.project_repo = repo
      self.project_root = repo.dirname
      return
    end

    parent = parent.dirname
  end until parent.root?
end

#eval_bufferObject

Eval the value of VER::Buffer in toplevel binding. So while hacking VER you can dynamically reload parts of it.



5
6
7
8
# File 'lib/ver/methods/basic.rb', line 5

def eval_buffer
  message "Source #{uri}"
  TOPLEVEL_BINDING.eval(value.to_s)
end

#eventsObject



300
301
302
# File 'lib/ver/buffer.rb', line 300

def events
  major_mode.event_history
end

#finalize_open(syntax) ⇒ Object



499
500
501
502
503
504
505
506
# File 'lib/ver/buffer.rb', line 499

def finalize_open(syntax)
  VER.defer do
    syntax ? setup_highlight_for(syntax) : setup_highlight
    load_preferences
    apply_modeline
  end
  bind('<Map>'){ at_insert.see }
end

#forward_scroll(count = buffer.prefix_count) ⇒ Object



8
9
10
11
12
# File 'lib/ver/methods/move.rb', line 8

def forward_scroll(count = buffer.prefix_count)
  count_abs = count.abs
  yview_scroll(count_abs, :units)
  next_line(count_abs)
end

#handle_pending_syntax_highlightsObject



1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
# File 'lib/ver/buffer.rb', line 1005

def handle_pending_syntax_highlights
  ignore_tags = %w[ver.highlight.pending sel]

  tag_ranges('ver.highlight.pending').each do |range|
    from, to = range.first, range.last

    (tag_names(from) - ignore_tags).each do |tag_name|
      tag_from, _ = tag_prevrange(tag_name, from)
      from = tag_from if tag_from && from > tag_from
    end

    (tag_names(to) - ignore_tags).each do |tag_name|
      _, tag_to = tag_nextrange(tag_name, to)
      to = tag_to if tag_to && to < tag_to
    end

    from, to = index("#{from} linestart"), index("#{to} lineend")
    syntax.highlight(self, from.line - 1, from, to) if syntax
    @invalid_trailing_whitespace.refresh(from: from, to: to)
    @markup_underline_link.refresh(from: from, to: to)
    tag_remove('ver.highlight.pending', from, to)
  end
end

#hideObject



667
668
669
# File 'lib/ver/buffer.rb', line 667

def hide
  layout.hide
end

#insert(*args) ⇒ Object



382
383
384
385
386
387
388
# File 'lib/ver/buffer.rb', line 382

def insert(*args)
  return super unless undoer

  undo_record do |record|
    record.insert(*args)
  end
end

#insert=(position) ⇒ Object



362
363
364
# File 'lib/ver/buffer.rb', line 362

def insert=(position)
  at_insert.index = position
end

#load_infoObject



250
251
252
253
254
255
256
257
258
259
260
# File 'lib/ver/buffer.rb', line 250

def load_info
  file = VER.loadpath.first/'buffer_info.json'
  l "Loading Buffer info from: #{file}"
  JSON::Store.new(file.to_s, true).transaction do |buffer_info|
    if info = buffer_info[uri.to_s]
      return info
    end
  end

  return {}
end

#load_preferencesObject



973
974
975
976
977
978
979
980
981
982
# File 'lib/ver/buffer.rb', line 973

def load_preferences
  return unless syntax

  name = syntax.name
  return unless file = VER.find_in_loadpath("preferences/#{name}.rb")
  @preferences = eval(file.read)
  apply_preferences
rescue Errno::ENOENT, TypeError => ex
  VER.error(ex)
end

#load_snippetsObject



963
964
965
966
967
968
969
970
971
# File 'lib/ver/buffer.rb', line 963

def load_snippets
  return unless syntax

  name = syntax.name
  return unless file = VER.find_in_loadpath("snippets/#{name}.rb")
  @snippets = eval(file.read)
rescue Errno::ENOENT, TypeError => ex
  VER.error(ex)
end

#load_syntax(name) ⇒ Object



947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
# File 'lib/ver/buffer.rb', line 947

def load_syntax(name)
  return false unless syntax

  theme = syntax.theme

  if name.is_a?(Syntax)
    self.syntax = Syntax.new(name.name, theme)
  elsif found = Syntax.find(name)
    self.syntax = Syntax.new(name, theme)
  else
    return false
  end

  message "Syntax #{syntax.name} loaded"
end

#load_theme(name) ⇒ Object



937
938
939
940
941
942
943
944
945
# File 'lib/ver/buffer.rb', line 937

def load_theme(name)
  return unless syntax
  return unless found = Theme.find(name)

  syntax.theme = Theme.load(found)
  touch!('1.0', 'end')

  message "Theme #{found} loaded"
end

#lock_uri(uri, &block) ⇒ Object



508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
# File 'lib/ver/buffer.rb', line 508

def lock_uri(uri, &block)
  lock = uri_lockfile(uri)

  if lock.file? # omg, someone is using it!
    info  = Hash[lock.read.scan(/^(\w+):\s*(.*)$/)]
    ctime = lock.ctime
    pid   = info['pid']
    user  = info['user']
    uri   = info['uri']
    alive = info

    prompt = <<-TEXT.chomp
Found a lock file at: #{lock}
owned by: #{user}
used by: #{pid}
dated: #{ctime}
for: #{uri}

Another program may be editing the same file.
If this is the case, be careful not to end up with two different instances of the same file when making changes.
Close this buffer or continue with caution.

[O]pen Read-Only, [E]dit anyway, [D]elete lock, [Q]uit, [A]bort: 
    TEXT

    ask(prompt) do |answer, action|
      case action
      when :modified
        case answer
        when /o/i
          yield :read_only
          :abort
        when /e/i
          yield :shared
          :abort
        when /q/i
          yield :quit
          :abort
        when /a/i
          yield :abort
          :abort
        when /d/i
          VER.defer do
            lock.rm
            lock_uri(uri, &block)
          end
          :abort
        else
          warn('invalid answer')
          minibuf.answer = ''
        end
      end
    end
  else
    File.open(lock, 'w+') do |file|
      file.puts(
        "pid: #{Process.pid}",
        "uid: #{Process.uid}",
        "uri: #{uri}"
      )
    end

    yield :single
  end
end

#may_closeObject



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/ver/methods/save.rb', line 169

def may_close
  return yield if pristine? || persisted?

  question = "Save buffer #{uri} before closing? " \
             "[y]es [n]o [c]ancel: "

  ask question, value: 'y' do |answer, action|
    case action
    when :attempt
      case answer[0]
      when /y/i
        may_save{ yield if save }
        VER.message 'Saved'
        :abort
      when /n/i
        yield
        VER.message 'Closing without saving'
        :abort
      else
        VER.warn "Cancel closing"
        :abort
      end
    end
  end
end

#may_save(as = nil) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/ver/methods/save.rb', line 195

def may_save(as = nil)
  last    = store(:stat, :mtime)
  current = filename.mtime rescue nil

  # if we have two mtimes
  if last && current
    # save if they're the same
    return yield if last == current
  else
     # save since there is no previous one
    return yield
  end

  question = "The buffer #{uri} has changed since last save, " \
             "overwrite? [y]es [n]o: "
  ask question, value: 'n' do |answer, action|
    case action
    when :attempt
      case answer[0]
      when /y/i
        yield
        :abort
      else
        warn "Save aborted"
        :abort
      end
    end
  end
end

#message(*args) ⇒ Object



308
309
310
# File 'lib/ver/buffer.rb', line 308

def message(*args)
  @minibuf.message(*args)
end

#name=(name) ⇒ Object



352
353
354
355
# File 'lib/ver/buffer.rb', line 352

def name=(name)
  @name = name
  status.event :filename if status
end

#on_destroy(event) ⇒ Object



224
225
226
227
228
229
230
231
232
# File 'lib/ver/buffer.rb', line 224

def on_destroy(event)
  @lock = true
  frame.destroy
  VER.cancel_block(@highlighter)
  VER.buffers.delete(self)
  unlock_uri(uri)
ensure
  VER.defer{ VER.exit if VER.buffers.empty? }
end

#on_enter_minor_mode(event) ⇒ Object



193
194
195
196
# File 'lib/ver/buffer.rb', line 193

def on_enter_minor_mode(event)
  sync_mode_status
  sync_mode_style(event.detail)
end

#on_focus_in(event) ⇒ Object



209
210
211
212
213
214
215
216
217
218
# File 'lib/ver/buffer.rb', line 209

def on_focus_in(event)
  if @minibuf.asking
    @minibuf.focus
  else
    Dir.chdir(filename.dirname.to_s) if filename && options.auto_chdir
    on_movement(event)
  end

  Tk.callback_break
end

#on_focus_out(event) ⇒ Object



220
221
222
# File 'lib/ver/buffer.rb', line 220

def on_focus_out(event)
  Tk.callback_break
end

#on_modified(event) ⇒ Object



198
199
200
# File 'lib/ver/buffer.rb', line 198

def on_modified(event)
  on_movement(event)
end

#on_movement(event) ⇒ Object



202
203
204
205
206
207
# File 'lib/ver/buffer.rb', line 202

def on_movement(event)
  at_insert.see
  at_sel.refresh
  @matching_brace.refresh
  sync_position_status
end

#open(uri, line = 1, char = 0) ⇒ Object



390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/ver/buffer.rb', line 390

def open(uri, line = 1, char = 0)
  case uri
  when Symbol
    open_symbolic(uri, line, char)
  when Pathname
    open_pathname(uri, line, char)
  when String, URI
    open_uri(uri, line, char)
  else
    raise ArgumentError, "Invalid uri: %p" % [uri]
  end

  true
end

#open_empty(line, char) ⇒ Object



463
464
465
466
467
468
469
# File 'lib/ver/buffer.rb', line 463

def open_empty(line, char)
  self.uri = '<empty>'
  self.value = ''
  self.encoding = value.encoding
  self.readonly = false
  after_open(nil, line, char)
end

#open_pathname(pathname, line, char) ⇒ Object



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# File 'lib/ver/buffer.rb', line 435

def open_pathname(pathname, line, char)
  lock_uri(pathname) do |answer|
    begin
      case answer
      when :single, :shared, :read_only
        self.uri = self.filename = pathname

        content, encoding = pathname.read_encoded_file
        self.encoding = encoding
        self.value = content.chomp
        self.readonly = answer == :read_only || pathname.readonly?
        self.locked = answer == :single
        detect_project_paths
        update_mtime
        after_open(nil, line, char)
      when :abort
        close
        :abort
      when :quit
        VER.exit
      end
    rescue Errno::ENOENT => ex
      VER.error(ex)
      open_empty(line, char)
    end
  end
end

#open_symbolic(symbol, line, char) ⇒ Object



405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/ver/buffer.rb', line 405

def open_symbolic(symbol, line, char)
  self.uri = symbol
  self.symbolic = true

  case symbol
  when :Scratch
    self.value = <<-TEXT
# This buffer is for notes you don't want to save, and for Ruby evaluation.
# If you want to create a file, visit that file with :o.
# then enter the text in that file's own buffer.
    TEXT
    syntax = 'Ruby'
  when :Messages
    clear
    tag(:info).configure(foreground: '#aaf')
    tag(:warn).configure(foreground: '#faa')
    syntax = 'Plain Text'
  when :Completions
    clear
    self.major_mode = :Completions
    tag('ver.minibuf.completion').configure(foreground: '#fff')
    syntax = 'Plain Text'
  else
    clear
    syntax = 'Plain Text'
  end

  after_open(Syntax.new(syntax), line, char)
end

#open_uri(uri, line, char) ⇒ Object



471
472
473
474
475
# File 'lib/ver/buffer.rb', line 471

def open_uri(uri, line, char)
  self.uri = uri
  self.value = Kernel.open(uri){|io| io.read.chomp }
  after_open(nil, line, char)
end

#parse_go_line(input) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/ver/methods/move.rb', line 45

def parse_go_line(input)
  case input.to_s.strip
  when /^\+(\d+)(?:,(\d+))?$/
    line, char = $1.to_i, $2.to_i
    yield index("#{at_insert.line + line}.#{char}")
  when /^(\d+)(?:,(\d+))?$/
    line, char = $1.to_i, $2.to_i
    yield index("#{line}.#{char}")
  else
    VER.warn("Invalid [+]line[,char]: %p" % [input])
  end
end

#persist_infoObject

This has to be called before <Destroy> is received, otherwise the Buffer is half-dead.



236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/ver/buffer.rb', line 236

def persist_info
  file = VER.loadpath.first/'buffer_info.json'
  l "Persisting Buffer info into: #{file}"

  JSON::Store.new(file.to_s, true).transaction do |buffer_info|
    syntax_name = @syntax.name if @syntax

    buffer_info[uri.to_s] = {
      'insert' => index('insert').to_s,
      'syntax' => syntax_name
    }
  end
end

#persisted?Boolean

Returns:

  • (Boolean)


316
317
318
319
320
321
322
323
324
# File 'lib/ver/buffer.rb', line 316

def persisted?
  return false unless filename
  return false unless filename.file?
  require 'digest/md5'

  on_disk = Digest::MD5.hexdigest(filename.read)
  in_memory = Digest::MD5.hexdigest(value)
  on_disk == in_memory
end

#prefix_arg_solObject



3
4
5
6
# File 'lib/ver/methods/move.rb', line 3

def prefix_arg_sol
  return if update_prefix_arg
  at_insert.start_of_line
end

#prefix_count(default = 1) ⇒ Object

Same as [prefix_arg], but returns 1 if there is no argument.

The return value in case there is no argument can be changed by passing a default argument.

Useful for [Move] methods and the like. Please note that calling this method is destructive. It will reset the state of the prefix_arg in order to avoid persistent arguments. So use it only once while your action is running, and store the result in a variable if you need it more than once.



760
761
762
763
764
765
766
767
768
769
770
# File 'lib/ver/buffer.rb', line 760

def prefix_count(default = 1)
  if skip_prefix_count_once
    self.skip_prefix_count_once = false
    update_prefix_arg
    return default
  end

  count = prefix_arg || default
  update_prefix_arg
  count
end

#quitObject

Close one buffer after another, wait when one needs user input.



20
21
22
23
24
25
26
27
28
29
# File 'lib/ver/methods/save.rb', line 20

def quit
  buffers = VER.buffers.each
  closer = lambda{
    begin
      buffers.next.close(&closer)
    rescue StopIteration
    end
  }
  closer.call
end

#redoObject



691
692
693
# File 'lib/ver/buffer.rb', line 691

def redo
  undoer.redo if undoer
end

#repeat_actionObject



772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
# File 'lib/ver/buffer.rb', line 772

def repeat_action
  stack = []

  actions.reverse_each do |event, mode, action|
    if stack.empty?
      next if REPEAT_BREAK_CMD.include?(action.to_method(self).name)
      next if REPEAT_BREAK_MODE.include?(mode.name)
    else
      break if REPEAT_BREAK_CMD.include?(action.to_method(self).name)
      break if REPEAT_BREAK_MODE.include?(mode.name)
    end

    stack << [event, action]
  end

  return if stack.empty?

  # make the argument the same for all called actions
  prefix_arg = self.prefix_arg

  stack.reverse_each do |event, action|
    self.prefix_arg = prefix_arg
    action.call(event)
  end
end

#replace(*args) ⇒ Object



374
375
376
377
378
379
380
# File 'lib/ver/buffer.rb', line 374

def replace(*args)
  return super unless undoer

  undo_record do |record|
    record.replace(*args)
  end
end

#rsearch_all(regexp, start = 'end', stop = '1.0') ⇒ Object



841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
# File 'lib/ver/buffer.rb', line 841

def rsearch_all(regexp, start = 'end', stop = '1.0')
  unless block_given?
    return Enumerator.new(self, :rsearch_all, regexp, start, stop)
  end

  while result = rsearch(regexp, start, stop, :count)
    pos, len = result
    break if !pos || len == 0

    from = index(pos)
    to   = index("#{pos} + #{len} chars")
    match = get(from, to)

    yield(match, from, to)

    start = from
  end
end

#save(filename = self.filename, &block) ⇒ Object



127
128
129
130
131
132
133
# File 'lib/ver/methods/save.rb', line 127

def save(filename = self.filename, &block)
  if filename
    save_to(filename, &block)
  else
    save_as(&block)
  end
end

#save_allObject

Save all buffers in ordered fashion, take into account that some buffers might require user interaction.



33
34
35
36
37
38
39
40
41
42
# File 'lib/ver/methods/save.rb', line 33

def save_all
  buffers = VER.buffers.each
  closer = lambda{
    begin
      buffers.next.save(&closer)
    rescue StopIteration
    end
  }
  closer.call
end

#save_asObject



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
167
# File 'lib/ver/methods/save.rb', line 142

def save_as
  if filename = self.filename
    dir = filename.dirname.to_s + '/'
  else
    dir = Dir.pwd + '/'
  end

  message = "Save #{uri} as: "

  ask message, value: dir do |answer, action|
    case action
    when :complete
      Pathname(answer + '*').expand_path.glob.map{|f|
        File.directory?(f) ? "#{f}/" : f
      }
    when :attempt
      begin
        save_to(Pathname(answer).expand_path)
        yield if block_given?
        :abort
      rescue => exception
        warn exception
      end
    end
  end
end

#save_atomic(from, to, &block) ⇒ Object

Try to copy the file we want to save to a temp dir, trying to preserve permissions. Once it’s there, we overwrite the contents with the buffer contents. If that worked, we move it to the original location.

On sshfs mounts we might face chown issues, so we ignore them. If this all fails for some reason, we resort to #save_dumb

When save was successful overall, we call the given block.



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
# File 'lib/ver/methods/save.rb', line 53

def save_atomic(from, to, &block)
  require 'tmpdir'
  sha1 = Digest::SHA1.hexdigest([from, to].join)
  temp_path = File.join(Dir.tmpdir, 'ver/save', sha1)
  temp_dir = File.dirname(temp_path)

  FileUtils.mkdir_p(temp_dir)
  FileUtils.copy_file(from, temp_path, preserve = true)
  if save_dumb(temp_path)
    FileUtils.mv(temp_path, to)
    success("Saved to #{to}", &block)
  else
    false
  end
rescue Errno::EACCES => ex
  # sshfs-mounts raise error but save correctly.
  if ex.backtrace[0].match(/chown\'$/)
    success("Saved to #{to} (chown issue)", &block)
  else
    warn(ex)
    false
  end
rescue Errno::ENOENT
  save_dumb(to, &block)
end

#save_dumb(to, &block) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ver/methods/save.rb', line 79

def save_dumb(to, &block)
  File.open(to, 'w+') do |io|
    io.set_encoding(self.encoding)

    begin
      io.write(self.value)
    rescue Encoding::UndefinedConversionError => ex
      # this might happen when trying to save UTF-8 as US-ASCII
      # so just warn, try to save as UTF-8 instead.
      warn("Saving as UTF-8 because of: #{ex.class}: #{ex}")
      io.rewind
      io.set_encoding(Encoding::UTF_8)
      io.write(self.value)
      self.encoding = Encoding::UTF_8
    end
  end

  success("Saved to #{to}", &block)
rescue Exception => ex
  warn(ex)
  VER.error(ex)
  return false
end

#save_popup(options = {}, &block) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/ver/methods/save.rb', line 111

def save_popup(options = {}, &block)
  options = options.dup
  options[:filetypes] ||= [
    ['ALL Files',  '*'    ],
    ['Text Files', '*.txt'],
  ]

  options[:initialfile]      ||= filename.basename
  options[:defaultextension] ||= filename.extname
  options[:initialdir]       ||= filename.dirname

  fpath = Tk.get_save_file(options)
  return unless fpath
  save_to(fpath, &block)
end

#save_to(to, &block) ⇒ Object



135
136
137
138
139
140
# File 'lib/ver/methods/save.rb', line 135

def save_to(to, &block)
  may_save{ save_atomic(filename, to, &block) }
rescue => exception
  VER.error(exception)
  may_save{ save_dumb(to, &block) }
end

#search_all(regexp, start = '1.0', stop = 'end - 1 chars') ⇒ Object



822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
# File 'lib/ver/buffer.rb', line 822

def search_all(regexp, start = '1.0', stop = 'end - 1 chars')
  unless block_given?
    return Enumerator.new(self, :search_all, regexp, start, stop)
  end

  while result = search(regexp, start, stop, :count)
    pos, len = result
    return if !pos || len == 0

    from  = index(pos)
    to    = index("#{pos} + #{len} chars")
    match = get(from, to)

    yield(match, from, to)

    start = to
  end
end

#set(option, value) ⇒ Object



644
645
646
647
648
649
650
651
652
653
654
655
656
657
# File 'lib/ver/buffer.rb', line 644

def set(option, value)
  method = "set_#{option}"

  if respond_to?(method)
    if block_given?
      __send__(method, value, &Proc.new)
    else
      __send__(method, value)
    end
  else
    options[option] = value
    yield(value) if block_given?
  end
end

#set_filetype(type) ⇒ Object



659
660
661
662
663
664
665
# File 'lib/ver/buffer.rb', line 659

def set_filetype(type)
  syntax = VER::Syntax.from_filename(Pathname("foo.#{type}"))

  if load_syntax(syntax)
    options.filetype = type
  end
end

#setupObject



125
126
127
128
129
130
131
132
# File 'lib/ver/buffer.rb', line 125

def setup
  setup_tags
  setup_misc
  setup_widgets
  setup_layout
  setup_binds
  layout.bind('<Map>'){ Tk.update }
end

#setup_bindsObject



178
179
180
181
182
183
184
185
# File 'lib/ver/buffer.rb', line 178

def setup_binds
  bind('<<EnterMinorMode>>', &method(:on_enter_minor_mode))
  bind('<<Modified>>',       &method(:on_modified))
  bind('<<Movement>>',       &method(:on_movement))
  bind('<FocusIn>',          &method(:on_focus_in))
  bind('<FocusOut>',         &method(:on_focus_out))
  bind('<Destroy>',          &method(:on_destroy))
end

#setup_highlightObject



914
915
916
# File 'lib/ver/buffer.rb', line 914

def setup_highlight
  setup_highlight_for(Syntax.from_filename(filename)) if filename
end

#setup_highlight_for(syntax) ⇒ Object



918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
# File 'lib/ver/buffer.rb', line 918

def setup_highlight_for(syntax)
  return if encoding == Encoding::BINARY
  return unless syntax
  syntax = Syntax.new(syntax) unless syntax.is_a?(Syntax)

  self.syntax = syntax
  VER.cancel_block(@highlighter)

  interval = options.syntax_highlight_interval.to_int
  @highlighter = VER.when_inactive_for(interval){
    handle_pending_syntax_highlights
  }

  touch!('1.0', 'end')
  handle_pending_syntax_highlights

  sync_mode_status
end

#setup_layoutObject



149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/ver/buffer.rb', line 149

def setup_layout
  self.    grid_configure row: 0, column: 0, sticky: :nsew
  @ybar.   grid_configure row: 0, column: 1, sticky: :ns if @ybar
  @xbar.   grid_configure row: 1, column: 0, sticky: :ew if @xbar
  status.  grid_configure row: 2, column: 0, sticky: :ew
  @minibuf.grid_configure row: 3, column: 0, sticky: :ew

  frame.grid_columnconfigure 0, weight: 1
  frame.grid_columnconfigure 1, weight: 0
  frame.grid_rowconfigure    0, weight: 1
  frame.grid_rowconfigure    1, weight: 0
  frame.grid_rowconfigure    2, weight: 0
  frame.grid_rowconfigure    3, weight: 0
end

#setup_miscObject



164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/ver/buffer.rb', line 164

def setup_misc
  @store_hash     = Hash.new{|h,k| h[k] = {} }
  @options        = Options.new(:text, VER.options)
  @at_current     = Mark.new(self, :current)
  @at_insert      = Insert.new(self)
  @at_end         = End.new(self)
  @at_sel         = Selection::Char.new(self, false)
  @theme_config   = nil
  @highlighter    = nil
  @pristine       = true
  self.register   = Register['*']
  self.major_mode = :Fundamental
end

#setup_tagsObject



187
188
189
190
191
# File 'lib/ver/buffer.rb', line 187

def setup_tags
  @invalid_trailing_whitespace = InvalidTrailingWhitespace.new(self)
  @markup_underline_link       = MarkupUnderlineLink.new(self)
  @matching_brace              = MatchingBrace.new(self)
end

#setup_widgetsObject



134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/ver/buffer.rb', line 134

def setup_widgets
  @status    = Status.new(frame, self)
  @minibuf   = VER.minibuf.peer_create(frame, self)

  if options.horizontal_scrollbar
    @xbar = Tk::Tile::XScrollbar.new(frame)
    xscrollbar(@xbar)
  end

  if options.vertical_scrollbar
    @ybar = Tk::Tile::YScrollbar.new(frame)
    yscrollbar(@ybar)
  end
end

#short_filenameObject



330
331
332
333
334
335
336
337
338
339
340
# File 'lib/ver/buffer.rb', line 330

def short_filename
  if filename
    if root = @project_root
      filename.relative_path_from(root).to_s
    else
      filename.sub(Dir.pwd + '/', '').to_s
    end
  elsif uri
    uri.to_s
  end
end

#showObject



671
672
673
# File 'lib/ver/buffer.rb', line 671

def show
  layout.show
end

#store(namespace, key, value = None) ⇒ Object



703
704
705
706
707
708
709
# File 'lib/ver/buffer.rb', line 703

def store(namespace, key, value = None)
  if None == value
    @store_hash[namespace][key]
  else
    @store_hash[namespace][key] = value
  end
end

#success(message) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/ver/methods/save.rb', line 103

def success(message)
  self.message(message)
  self.pristine = true
  update_mtime
  yield if block_given?
  true
end

#sync_encoding_statusObject



270
271
272
# File 'lib/ver/buffer.rb', line 270

def sync_encoding_status
  status.event :encoding
end

#sync_mode_statusObject



262
263
264
# File 'lib/ver/buffer.rb', line 262

def sync_mode_status
  status.event :mode
end

#sync_mode_style(given_mode = nil) ⇒ Object



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/ver/buffer.rb', line 278

def sync_mode_style(given_mode = nil)
  config = (theme_config || {}).merge(blockcursor: false)

  modes = given_mode ? [given_mode] : major_mode.minors

  modes.each do |mode|
    mode = MinorMode[mode]

    MODE_STYLES.each do |pattern, style|
      config.merge!(style) if pattern === mode.name
    end
  end

  configure(config)
  return unless status && color = config[:insertbackground]

  status.style = {
    background: cget(:background),
    foreground: color,
  }
end

#sync_percent_statusObject



274
275
276
# File 'lib/ver/buffer.rb', line 274

def sync_percent_status
  status.event :percent
end

#sync_position_statusObject



266
267
268
# File 'lib/ver/buffer.rb', line 266

def sync_position_status
  status.event :position, :percent
end

#tag_all_matching(name, regexp, options = {}) ⇒ Object



804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
# File 'lib/ver/buffer.rb', line 804

def tag_all_matching(name, regexp, options = {})
  name = name.to_s
  options = TAG_ALL_MATCHING_OPTIONS.merge(options)
  from, to = options.values_at(:from, :to)

  if tag_exists?(name)
    tag_remove(name, from, to)
  else
    fg, bg = options.values_at(:foreground, :background)
    tag_configure(name, foreground: fg, background: bg)
  end

  search_all(regexp, from, to) do |match, match_from, match_to|
    name = yield(match, match_from, match_to) if block_given?
    tag_add name, match_from, match_to
  end
end

#tag_exists?(given_path) ⇒ Boolean

Returns:

  • (Boolean)


798
799
800
801
802
# File 'lib/ver/buffer.rb', line 798

def tag_exists?(given_path)
  tag_names.include?(given_path)
rescue RuntimeError => ex
  false
end

#tags_tooltip(count = prefix_arg) ⇒ Object

Display a tooltip for the tags the insert cursor is on. Also generates a status message. Hides the tooltip after 5 seconds unless a seconds count is given.



13
14
15
16
17
18
19
# File 'lib/ver/methods/basic.rb', line 13

def tags_tooltip(count = prefix_arg)
  index = at_insert
  value = index.tag_names.join(', ')

  message(value)
  tooltip(value, count || 5)
end

#tooltip(string, timeout = 5) ⇒ Object



21
22
23
24
25
26
27
# File 'lib/ver/methods/basic.rb', line 21

def tooltip(string, timeout = 5)
  require 'ver/tooltip'

  tooltip = Tk::Tooltip.new(string)
  tooltip.show_on(self)
  Tk::After.ms(timeout * 1000){ tooltip.destroy }
end

#touch!(*indices) ⇒ Object



682
683
684
685
# File 'lib/ver/buffer.rb', line 682

def touch!(*indices)
  tag_add('ver.highlight.pending', *indices.flatten)
  Tk::Event.generate(self, '<<Modified>>')
end

#type(string) ⇒ Object



326
327
328
# File 'lib/ver/buffer.rb', line 326

def type(string)
  major_mode.fake(string)
end

#undoObject



687
688
689
# File 'lib/ver/buffer.rb', line 687

def undo
  undoer.undo if undoer
end

#undo_record(&block) ⇒ Object

Use this method in commands that do multiple insert, delete, replace



696
697
698
699
700
701
# File 'lib/ver/buffer.rb', line 696

def undo_record(&block)
  VER.warn "Buffer is Read-only" if readonly
  undoer ? undoer.record_multi(&block) : yield(self)
ensure
  self.pristine = false
end

#unlock_uri(uri = self.uri) ⇒ Object



574
575
576
577
# File 'lib/ver/buffer.rb', line 574

def unlock_uri(uri = self.uri)
  return unless locked?
  uri_lockfile(uri).rm_f
end

#up_down_displayline(count) ⇒ Object

OK, finally found the issue.

the implementation of tk::TextUpDownLine is smart, but not smart enough. It doesn’t assume large deltas between the start of up/down movement and the last other modification of the insert mark.

This means that, after scrolling with up/down for a few hundred lines, it has to calculate the amount of display lines in between, which is a very expensive calculation and time increases O(delta_lines).

We’ll try to solve this another way, by assuming that there are at least a few other lines of same or greater length in between, we simply compare against a closer position and make delta_lines as small as possible.

Now, if you go to, like column 100 of a line, and there is never a line as long for the rest of the file, the scrolling will still slow down a lot. This is an issue we can fix if we “forget” the @udl_pos_orig after a user-defined maximum delta (something around 200 should do), will implement that on demand.



880
881
882
883
884
885
886
887
888
889
890
891
# File 'lib/ver/buffer.rb', line 880

def up_down_displayline(count)
  insert = at_insert

  @udl_pos_orig = insert if @udl_pos_prev != insert

  lines = count(@udl_pos_orig, insert, :displaylines)
  target = index("#@udl_pos_orig + #{lines + count} displaylines")
  @udl_pos_prev = target

  @udl_pos_orig = target if target.char == @udl_pos_orig.char
  target
end

#up_down_line(count) ⇒ Object

This method goes up and down lines, not taking line wrapping into account. To go a line down, pass 1, to go one up -1, you get the idea. Tries to maintain the same char position across lines.



896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
# File 'lib/ver/buffer.rb', line 896

def up_down_line(count)
  insert = at_insert

  # if the last movement was not done by up_down_*, set new origin.
  @udl_pos_orig = insert if @udl_pos_prev != insert

  # count lines between origin and current insert position.
  lines = count(@udl_pos_orig, insert, :lines)
  # now get the target count lines below.
  target = index("#@udl_pos_orig + #{lines + count} lines")

  @udl_pos_prev = target

  # if target has the same char pos as the previous one, use it as origin.
  @udl_pos_orig = target if target.char == @udl_pos_orig.char
  target
end

#update_mtimeObject



587
588
589
590
# File 'lib/ver/buffer.rb', line 587

def update_mtime
  store(:stat, :mtime, filename.mtime) if filename
rescue Errno::ENOENT
end

#update_prefix_argObject



734
735
736
737
738
739
740
741
742
743
744
745
746
747
# File 'lib/ver/buffer.rb', line 734

def update_prefix_arg
  numbers = []

  events.reverse_each do |event|
    break unless event.keysym =~ /^(\d+)$/
    numbers << $1
  end

  if numbers.any? && numbers != ['0']
    self.prefix_arg = numbers.reverse.join.to_i
  else
    self.prefix_arg = nil
  end
end

#uri_lockfile(uri = self.uri) ⇒ Object



579
580
581
582
583
584
585
# File 'lib/ver/buffer.rb', line 579

def uri_lockfile(uri = self.uri)
  require 'tmpdir'
  hash = Digest::SHA1.hexdigest(uri.to_s)
  lock = Pathname.tmpdir/'ver/lock'/hash
  lock.dirname.mkpath
  lock
end

#warn(*args) ⇒ Object



312
313
314
# File 'lib/ver/buffer.rb', line 312

def warn(*args)
  @minibuf.warn(*args)
end

#with_registerObject



725
726
727
728
729
730
731
732
# File 'lib/ver/buffer.rb', line 725

def with_register
  @with_register_level ||= 0
  @with_register_level += 1
  value = yield(register)
  @with_register_level -= 1
ensure
  self.register = Register['*'] if @with_register_level == 0
end