Class: Diakonos::Buffer

Inherits:
Object show all
Defined in:
lib/diakonos/buffer.rb,
lib/diakonos/buffer/file.rb,
lib/diakonos/buffer/undo.rb,
lib/diakonos/buffer/cursor.rb,
lib/diakonos/buffer/delete.rb,
lib/diakonos/buffer/display.rb,
lib/diakonos/buffer/searching.rb,
lib/diakonos/buffer/selection.rb,
lib/diakonos/buffer/bookmarking.rb,
lib/diakonos/buffer/indentation.rb

Constant Summary collapse

STOPPED_TYPING =
true
STILL_TYPING =
false
NO_SNAPSHOT =
true
DO_DISPLAY =
true
DONT_DISPLAY =
false
READ_ONLY =
true
READ_WRITE =
false
ROUND_DOWN =
false
ROUND_UP =
true
PAD_END =
true
DONT_PAD_END =
false
START_FROM_BEGINNING =
-1
DO_PITCH_CURSOR =
true
DONT_PITCH_CURSOR =
false
STRIP_LINE =
true
DONT_STRIP_LINE =
false
USE_INDENT_IGNORE =
true
DONT_USE_INDENT_IGNORE =
false
WORD_REGEXP =
/\w+/
SECONDS_PER_DAY =
60 * 60 * 24
DO_USE_MD5 =
true
DONT_USE_MD5 =
false
COLUMNS_FOR_DIAGNOSTICS =

width in characters

1
CHARACTER_PAIRS =
{
  '(' => { partner: ')', direction: :forward },
  '<' => { partner: '>', direction: :forward },
  '{' => { partner: '}', direction: :forward },
  '[' => { partner: ']', direction: :forward },
  ')' => { partner: '(', direction: :backward },
  '>' => { partner: '<', direction: :backward },
  '}' => { partner: '{', direction: :backward },
  ']' => { partner: '[', direction: :backward },
}
OPPOSITE_DIRECTION =
{
  up: :down,
  down: :up,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Buffer

Set name to nil to create a buffer that is not associated with a file.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • 'filepath' (String)

    A file path (which is expanded internally)

  • 'read_only' (Boolean) — default: READ_WRITE

    Whether the buffer should be protected from modification

  • 'cursor' (Hash)

    A Hash containing ‘row’ and/or ‘col’ indicating where the cursor should initially be placed. Defaults: 0 and 0

  • 'display' (Hash)

    A Hash containing ‘top_line’ and ‘left_column’ indicating where the view should be positioned in the file. Defaults: 0 and 0

See Also:



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
# File 'lib/diakonos/buffer.rb', line 52

def initialize( options = {} )
  @name = options[ 'filepath' ]
  @modified = false
  @time_last_viewed = Time.now

  @buffer_states = Array.new
  @cursor_states = Array.new
  if @name.nil?
    @lines = Array.new
    @lines[ 0 ] = ""
    @last_modification_check = Time.now
  else
    @name = File.expand_path( @name )
    if FileTest.exist? @name
      @lines = IO.readlines( @name )
      if ( @lines.empty? ) || ( @lines[ -1 ][ -1.. ] == "\n" )
        @lines.push ""
      end
      @lines = @lines.collect(&:chomp)
      @last_modification_check = File.mtime(@name)
    else
      @lines = Array.new
      @lines[ 0 ] = ""
      @last_modification_check = Time.now
    end
  end

  @current_buffer_state = 0

  options[ 'display' ] ||= Hash.new
  @top_line = options[ 'display' ][ 'top_line' ] || 0
  @left_column = options[ 'display' ][ 'left_column' ] ||  0
  @desired_column = @left_column
  @mark_anchor = nil
  @text_marks = Hash.new
  @selection_mode = :normal
  @last_search_regexps = Array( options['last_search_regexps'] ).map { |r| Regexp.new(r) }
  @highlight_regexp = nil
  @last_search = nil
  @lsp_session = nil
  @changing_selection = false
  @typing = false
  options[ 'cursor' ] ||= Hash.new
  @last_col = options[ 'cursor' ][ 'col' ] || 0
  @last_row = options[ 'cursor' ][ 'row' ] || 0
  @last_screen_y = @last_row - @top_line
  @last_screen_x = @last_col - @left_column
  @last_screen_col = @last_screen_x
  @read_only = options[ 'read_only' ] || READ_WRITE
  @bookmarks = Array.new
  @highlight_cache = []
  @highlight_cache_valid_to = -1
  @lang_stack = Array.new

  configure

  if @settings[ "convert_tabs" ]
    tabs_subbed = false
    @lines.collect! do |line|
      new_line = line.expand_tabs( @tab_size )
      tabs_subbed = ( tabs_subbed || new_line != line )
      # Return value for collect:
      new_line
    end
    @modified = ( @modified || tabs_subbed )
    if tabs_subbed
      $diakonos.set_iline "(spaces substituted for tab characters)"
    end
  end

  @buffer_states[ @current_buffer_state ] = @lines
  @cursor_states[ @current_buffer_state ] = [ @last_row, @last_col ]
end

Instance Attribute Details

#changing_selectionObject (readonly)

Returns the value of attribute changing_selection.



5
6
7
# File 'lib/diakonos/buffer.rb', line 5

def changing_selection
  @changing_selection
end

#desired_column=(value) ⇒ Object (writeonly)

Sets the attribute desired_column

Parameters:

  • value

    the value to set the attribute desired_column to.



6
7
8
# File 'lib/diakonos/buffer.rb', line 6

def desired_column=(value)
  @desired_column = value
end

#languageObject (readonly)

Returns the value of attribute language.



5
6
7
# File 'lib/diakonos/buffer.rb', line 5

def language
  @language
end

#last_colObject (readonly)

Returns the value of attribute last_col.



5
6
7
# File 'lib/diakonos/buffer/cursor.rb', line 5

def last_col
  @last_col
end

#last_rowObject (readonly)

Returns the value of attribute last_row.



5
6
7
# File 'lib/diakonos/buffer/cursor.rb', line 5

def last_row
  @last_row
end

#last_screen_colObject (readonly)

Returns the value of attribute last_screen_col.



5
6
7
# File 'lib/diakonos/buffer/cursor.rb', line 5

def last_screen_col
  @last_screen_col
end

#last_screen_xObject (readonly)

Returns the value of attribute last_screen_x.



5
6
7
# File 'lib/diakonos/buffer/cursor.rb', line 5

def last_screen_x
  @last_screen_x
end

#last_screen_yObject (readonly)

Returns the value of attribute last_screen_y.



5
6
7
# File 'lib/diakonos/buffer/cursor.rb', line 5

def last_screen_y
  @last_screen_y
end

#last_search_regexpsObject (readonly)

Returns the value of attribute last_search_regexps.



5
6
7
# File 'lib/diakonos/buffer/searching.rb', line 5

def last_search_regexps
  @last_search_regexps
end

#left_columnObject (readonly)

Returns the value of attribute left_column.



6
7
8
# File 'lib/diakonos/buffer/display.rb', line 6

def left_column
  @left_column
end

#linesObject

Returns the value of attribute lines.



4
5
6
# File 'lib/diakonos/buffer.rb', line 4

def lines
  @lines
end

#lsp_sessionObject

Returns the value of attribute lsp_session.



4
5
6
# File 'lib/diakonos/buffer.rb', line 4

def lsp_session
  @lsp_session
end

#nameObject (readonly)

Returns the value of attribute name.



5
6
7
# File 'lib/diakonos/buffer.rb', line 5

def name
  @name
end

#num_matches_foundObject (readonly)

Returns the value of attribute num_matches_found.



5
6
7
# File 'lib/diakonos/buffer/searching.rb', line 5

def num_matches_found
  @num_matches_found
end

#original_languageObject (readonly)

Returns the value of attribute original_language.



5
6
7
# File 'lib/diakonos/buffer.rb', line 5

def original_language
  @original_language
end

#read_onlyObject

Returns the value of attribute read_only.



4
5
6
# File 'lib/diakonos/buffer.rb', line 4

def read_only
  @read_only
end

#selection_modeObject (readonly)

Returns the value of attribute selection_mode.



5
6
7
# File 'lib/diakonos/buffer.rb', line 5

def selection_mode
  @selection_mode
end

#tab_sizeObject (readonly)

Returns the value of attribute tab_size.



5
6
7
# File 'lib/diakonos/buffer.rb', line 5

def tab_size
  @tab_size
end

#top_lineObject (readonly)

Returns the value of attribute top_line.



6
7
8
# File 'lib/diakonos/buffer/display.rb', line 6

def top_line
  @top_line
end

Instance Method Details

#==(other) ⇒ Object



178
179
180
181
182
# File 'lib/diakonos/buffer.rb', line 178

def ==(other)
  return false  if other.nil?

  @name == other.name
end

#[](arg) ⇒ Object



174
175
176
# File 'lib/diakonos/buffer.rb', line 174

def []( arg )
  @lines[ arg ]
end

#_find_down(regexps, regexp, from_row, from_col, search_area) ⇒ Object



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
# File 'lib/diakonos/buffer/searching.rb', line 66

def _find_down(regexps, regexp, from_row, from_col, search_area)
  # Check the current row first.

  index = @lines[ from_row ].index(
    regexp,
    ( @last_finding ? @last_finding.start_col : from_col ) + 1
  )
  if index
    finding = confirm_finding( regexps, search_area, from_row, index, Regexp.last_match )
    return finding  if finding
  end

  # Check below the cursor.

  ( (from_row + 1)..search_area.end_row ).each do |i|
    line = @lines[ i ]
    if i == search_area.end_row
      line = line[ 0...search_area.end_col ]
    end
    index = line.index( regexp )
    if index
      finding = confirm_finding( regexps, search_area, i, index, Regexp.last_match )
      return finding  if finding
    end
  end

  if index
    finding = confirm_finding( regexps, search_area, search_area.end_row, index, Regexp.last_match )
    return finding  if finding
  end

  # Wrap around.

  @wrapped = true

  index = @lines[ search_area.start_row ].index( regexp, search_area.start_col )
  if index
    finding = confirm_finding( regexps, search_area, search_area.start_row, index, Regexp.last_match )
    return finding  if finding
  end

  ( search_area.start_row+1...from_row ).each do |i|
    index = @lines[ i ].index( regexp )
    if index
      finding = confirm_finding( regexps, search_area, i, index, Regexp.last_match )
      return finding  if finding
    end
  end

  # And finally, the other side of the current row.

  if from_row == search_area.start_row
    index_col = search_area.start_col
  else
    index_col = 0
  end

  index = @lines[from_row].index(regexp, index_col)
  if index && index <= (@last_finding ? @last_finding.start_col : from_col)
    confirm_finding( regexps, search_area, from_row, index, Regexp.last_match )
  end
end

#_find_up(regexps, regexp, search_area, from_row, from_col) ⇒ Object



129
130
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
165
166
167
168
169
# File 'lib/diakonos/buffer/searching.rb', line 129

def _find_up(regexps, regexp, search_area, from_row, from_col)
  # Check the current row first.

  col_to_check = ( @last_finding ? @last_finding.end_col : from_col ) - 1
  if ( col_to_check >= 0 ) && ( index = @lines[ from_row ][ 0...col_to_check ].rindex( regexp ) )
    finding = confirm_finding( regexps, search_area, from_row, index, Regexp.last_match )
    return finding  if finding
  end

  # Check above the cursor.

  (from_row - 1).downto( 0 ) do |i|
    index = @lines[i].rindex(regexp)
    if index
      finding = confirm_finding( regexps, search_area, i, index, Regexp.last_match )

      return finding  if finding
    end
  end

  # Wrap around.

  @wrapped = true

  (@lines.length - 1).downto(from_row + 1) do |i|
    index = @lines[i].rindex(regexp)
    if index
      finding = confirm_finding( regexps, search_area, i, index, Regexp.last_match )

      return finding  if finding
    end
  end

  # And finally, the other side of the current row.

  search_col = ( @last_finding ? @last_finding.start_col : from_col ) + 1
  index = @lines[ from_row ].rindex( regexp )
  if index && index > search_col
    confirm_finding( regexps, search_area, from_row, index, Regexp.last_match )
  end
end

#anchor_selection(row = @last_row, col = @last_col, do_display = DO_DISPLAY) ⇒ Object



192
193
194
195
196
197
198
199
200
# File 'lib/diakonos/buffer/selection.rb', line 192

def anchor_selection( row = @last_row, col = @last_col, do_display = DO_DISPLAY )
  @mark_anchor = ( @mark_anchor || Hash.new )
  @mark_anchor[ "row" ] = row
  @mark_anchor[ "col" ] = col
  record_mark_start_and_end
  @changing_selection = true
  @auto_anchored = false
  display  if do_display
end

#anchor_unanchored_selection(*args) ⇒ Object



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

def anchor_unanchored_selection(*args)
  if @mark_anchor.nil?
    anchor_selection(*args)
    @changing_selection = false
  end
  @auto_anchored = true
end

#carriage_returnObject



408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/diakonos/buffer.rb', line 408

def carriage_return
  take_snapshot
  row = @last_row
  col = @last_col
  @lines = @lines[ 0...row ] +
    [ @lines[ row ][ 0...col ] ] +
    [ @lines[ row ][ col.. ] ] +
    @lines[ (row+1).. ]
  cursor_to( row + 1, 0 )
  if @auto_indent
    parsed_indent  undoable: false
  end
  set_modified modified_from_line: row
end

#clear_matches(do_display = DONT_DISPLAY) ⇒ Object



366
367
368
369
370
# File 'lib/diakonos/buffer/searching.rb', line 366

def clear_matches( do_display = DONT_DISPLAY )
  @text_marks[ :found ] = []
  @highlight_regexp = nil
  display  if do_display
end

#clear_pair_highlightObject



453
454
455
# File 'lib/diakonos/buffer/searching.rb', line 453

def clear_pair_highlight
  @text_marks[ :pair ] = nil
end

#clear_search_areaObject



27
28
29
30
31
# File 'lib/diakonos/buffer/searching.rb', line 27

def clear_search_area
  @search_area = nil
  @text_marks.delete :search_area_pre
  @text_marks.delete :search_area_post
end

#close_codeObject



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
# File 'lib/diakonos/buffer.rb', line 279

def close_code
  line = @lines[ @last_row ]
  @closers.each_value do |h|
    h[ :regexp ] =~ line
    lm = Regexp.last_match
    if lm
      str = (
        case h[ :closer ]
        when String
          if lm[ 1 ].nil?
            h[ :closer ]
          else
            lm[ 1 ].gsub(
              Regexp.new( "(#{ Regexp.escape( lm[1] ) })" ),
              h[ :closer ]
            )
          end
        when Proc
          h[ :closer ].call( lm ).to_s
        end
      )
      r, c = @last_row, @last_col
      paste str, do_parsed_indent: @indent_closers, typing: false
      cursor_to r, c
      if /%_/ === str
        find [/%_/], direction: :down, replacement: '', auto_choice: CHOICE_YES_AND_STOP
      end
    end
  end
end

#collapse_whitespaceObject



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/diakonos/buffer.rb', line 310

def collapse_whitespace
  if selection_mark
    remove_selection DONT_DISPLAY
  end

  line = @lines[ @last_row ]
  head = line[ 0...@last_col ]
  tail = line[ @last_col.. ]
  new_head = head.sub( /\s+$/, '' )
  new_line = new_head + tail.sub( /^\s+/, ' ' )
  if new_line != line
    take_snapshot(typing: true)
    @lines[ @last_row ] = new_line
    cursor_to( @last_row, @last_col - ( head.length - new_head.length ) )
    set_modified modified_from_line: @last_row
  end
end

#column_of(x) ⇒ Object

Translates the window column, x, to a buffer-relative column index.



452
453
454
# File 'lib/diakonos/buffer.rb', line 452

def column_of( x )
  @left_column + x
end

#column_to_x(col) ⇒ Object

Returns nil if the column is off-screen.



471
472
473
474
475
476
477
# File 'lib/diakonos/buffer.rb', line 471

def column_to_x( col )
  return nil if col.nil?

  x = col - @left_column
  x = nil if ( x < 0 ) || ( x > @left_column + Curses.cols - 1 )
  x
end

#columnize(delimiter = /=>?|:|,/, num_spaces_padding = 1) ⇒ Object



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/diakonos/buffer.rb', line 328

def columnize( delimiter = /=>?|:|,/, num_spaces_padding = 1 )
  take_snapshot

  lines = self.selected_lines
  column_width = 0
  lines.each do |line|
    pos = ( line =~ delimiter )
    if pos
      column_width = [ pos, column_width ].max
    end
  end

  padding = ' ' * num_spaces_padding
  one_modified = false

  lines.each do |line|
    old_line = line.dup
    if line =~ /^(.+?)(#{delimiter.source})(.*)$/
      pre = ::Regexp.last_match(1)
      del = ::Regexp.last_match(2)
      post = ::Regexp.last_match(3)
      if pre !~ /\s$/
        del = " #{del}"
      end
      if post !~ /^\s/
        del = "#{del} "
      end
      del.sub!( /^\s+/, padding )
      del.sub!( /\s+$/, padding )
      line.replace( ( "%-#{column_width}s" % pre ) + del + post )
    end
    one_modified ||= ( line != old_line )
  end

  if one_modified
    set_modified modified_from_line: (@text_marks[:selection]&.start_row || @last_row)
  end
end

#comment_outObject



367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/diakonos/buffer.rb', line 367

def comment_out
  take_snapshot

  one_modified = false
  closer = @settings[ "lang.#{@language}.comment_close_string" ].to_s

  self.selected_lines.each do |line|
    next  if line.strip.empty?

    old_line = line.dup
    line.gsub!( /^(\s*)/, "\\1" + @settings[ "lang.#{@language}.comment_string" ].to_s )
    if ! closer.empty? && line !~ /#{Regexp.escape(closer)}$/
      line << closer
    end
    one_modified ||= ( line != old_line )
  end

  if one_modified
    set_modified modified_from_line: (@text_marks[:selection]&.start_row || @last_row)
  end
end

#configure(language = ( $diakonos.get_language_from_shabang( @lines[ 0 ] ) || $diakonos.get_language_from_name( @name ) || LANG_TEXT )) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/diakonos/buffer.rb', line 126

def configure(
  language = (
    $diakonos.get_language_from_shabang( @lines[ 0 ] ) ||
    $diakonos.get_language_from_name( @name ) ||
    LANG_TEXT
  )
)
  reset_display
  set_language language
  @original_language = @language
  build_highlight_cache
end

#confirm_finding(regexps, search_area, from_row, from_col, match) ⇒ Object



49
50
51
52
53
# File 'lib/diakonos/buffer/searching.rb', line 49

def confirm_finding( regexps, search_area, from_row, from_col, match )
  found_text = match[0]
  range = ::Diakonos::Range.new(from_row, from_col, from_row, from_col + found_text.length)
  Finding.confirm(range, regexps, @lines, search_area, match)
end

#contextObject



628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
# File 'lib/diakonos/buffer.rb', line 628

def context
  retval = Array.new
  row = @last_row
  clevel = indentation_level( row )
  while row > 0 && clevel < 0
    row = row - 1
    clevel = indentation_level( row )
  end
  clevel = 0  if clevel < 0
  while row > 0
    row = row - 1
    line = @lines[ row ]
    if ! line.strip.empty? && ( line !~ @settings[ "lang.#{@language}.context.ignore" ] )
      level = indentation_level( row )
      if level < clevel && level > -1
        retval.unshift line
        clevel = level
        break  if clevel == 0
      end
    end
  end
  retval
end

#copy_selectionObject



228
229
230
# File 'lib/diakonos/buffer/selection.rb', line 228

def copy_selection
  selected_text
end

#current_columnObject



495
496
497
# File 'lib/diakonos/buffer.rb', line 495

def current_column
  @last_col
end

#current_lineObject



432
433
434
# File 'lib/diakonos/buffer.rb', line 432

def current_line
  @lines[ @last_row ]
end

#current_rowObject



479
480
481
# File 'lib/diakonos/buffer.rb', line 479

def current_row
  @last_row
end

#cursor_to(row, col, do_display = DONT_DISPLAY, stopped_typing = STOPPED_TYPING, adjust_row = ADJUST_ROW) ⇒ Object

Returns true iff the cursor changed positions in the buffer.



8
9
10
11
12
13
14
15
16
17
18
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
# File 'lib/diakonos/buffer/cursor.rb', line 8

def cursor_to( row, col, do_display = DONT_DISPLAY, stopped_typing = STOPPED_TYPING, adjust_row = ADJUST_ROW )
  old_last_row = @last_row
  old_last_col = @last_col

  row = NumberFitter.fit(
    number: row,
    min: 0,
    max: @lines.length - 1,
  )

  if col < 0
    if adjust_row
      if row > 0
        row = row - 1
        col = @lines[ row ].length
      else
        col = 0
      end
    else
      col = 0
    end
  elsif col > @lines[ row ].length
    if adjust_row
      if row < @lines.length - 1
        row = row + 1
        col = 0
      else
        col = @lines[ row ].length
      end
    else
      col = @lines[ row ].length
    end
  end

  if adjust_row
    @desired_column = col
  else
    goto_col = [ @desired_column, @lines[ row ].length ].min
    if col < goto_col
      col = goto_col
    end
  end

  new_col = tab_expanded_column( col, row )
  view_changed = show_character( row, new_col )
  @last_screen_y = row - @top_line
  @last_screen_x = new_col - @left_column

  @typing = false  if stopped_typing
  @last_row = row
  @last_col = col
  @last_screen_col = new_col

  record_mark_start_and_end

  selection_removed = false
  if ! @auto_anchored && ! @changing_selection && selecting?
    remove_selection( DONT_DISPLAY )
    selection_removed = true
  end
  @auto_anchored = false

  old_pair = @text_marks[ :pair ]
  if @settings[ 'view.pairs.highlight' ]
    highlight_pair
  elsif old_pair
    clear_pair_highlight
  end
  highlight_changed = old_pair != @text_marks[ :pair ]

  if selection_removed || ( do_display && ( selecting? || view_changed || highlight_changed ) )
    display
  else
    $diakonos.display_mutex.synchronize do
      @win_main.setpos( @last_screen_y, @last_screen_x )
    end
  end
  $diakonos.update_status_line
  $diakonos.update_context_line

  ( @last_row != old_last_row || @last_col != old_last_col )
end

#cursor_to_bolObject



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/diakonos/buffer/cursor.rb', line 95

def cursor_to_bol
  row = @last_row
  case @settings[ "bol_behaviour" ]
  when BOL_ZERO
    col = 0
  when BOL_FIRST_CHAR
    col = ( ( @lines[ row ] =~ /\S/ ) || 0 )
  when BOL_ALT_ZERO
    if @last_col == 0
      col = ( @lines[ row ] =~ /\S/ )
    else
      col = 0
    end
  # when BOL_ALT_FIRST_CHAR
  else
    first_char_col = ( ( @lines[ row ] =~ /\S/ ) || 0 )
    if @last_col == first_char_col
      col = 0
    else
      col = first_char_col
    end
  end
  cursor_to( row, col, DO_DISPLAY )
end

#cursor_to_bovObject

Bottom of view



151
152
153
# File 'lib/diakonos/buffer/cursor.rb', line 151

def cursor_to_bov
  cursor_to( row_of( 0 + $diakonos.main_window_height - 1 ), @last_col, DO_DISPLAY )
end

#cursor_to_eofObject



91
92
93
# File 'lib/diakonos/buffer/cursor.rb', line 91

def cursor_to_eof
  cursor_to( @lines.length - 1, @lines[ -1 ].length, DO_DISPLAY )
end

#cursor_to_eolObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/diakonos/buffer/cursor.rb', line 120

def cursor_to_eol
  y = @win_main.cury
  end_col = line_at( y ).length
  last_char_col = line_at( y ).rstrip.length
  case @settings[ 'eol_behaviour' ]
  when EOL_END
    col = end_col
  when EOL_LAST_CHAR
    col = last_char_col
  when EOL_ALT_LAST_CHAR
    if @last_col == last_char_col
      col = end_col
    else
      col = last_char_col
    end
  else
    if @last_col == end_col
      col = last_char_col
    else
      col = end_col
    end
  end
  cursor_to( @last_row, col, DO_DISPLAY )
end

#cursor_to_tovObject

Top of view



146
147
148
# File 'lib/diakonos/buffer/cursor.rb', line 146

def cursor_to_tov
  cursor_to( row_of( 0 ), @last_col, DO_DISPLAY )
end

#deleteObject

x and y are given window-relative, not buffer-relative.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/diakonos/buffer/delete.rb', line 6

def delete
  if selection_mark
    delete_selection
  else
    row = @last_row
    col = @last_col
    if ( row >= 0 ) && ( col >= 0 )
      line = @lines[ row ]
      if col == line.length
        if row < @lines.length - 1
          # Delete newline, and concat next line
          join_lines( row )
          cursor_to( @last_row, @last_col )
        end
      else
        take_snapshot(typing: true)
        @lines[ row ] = line[ 0...col ] + line[ (col + 1).. ]
        set_modified modified_from_line: row
      end
    end
  end
end

#delete_from(char) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/diakonos/buffer/delete.rb', line 100

def delete_from( char )
  remove_selection( DONT_DISPLAY )  if selection_mark

  first_row = row = @last_row
  index = @lines[ @last_row ].rindex( char, @last_col-1 )

  while row > 0 && index.nil?
    row -= 1
    index = @lines[ row ].rindex( char )
  end

  if index
    deleted_text = delete_from_to( row, index+1, first_row, @last_col )
    cursor_to( row, index+1 )
    deleted_text
  end
end

#delete_from_to(row_from, col_from, row_to, col_to) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/diakonos/buffer/delete.rb', line 68

def delete_from_to( row_from, col_from, row_to, col_to )
  take_snapshot
  if row_to == row_from
    retval = [ @lines[ row_to ].slice!( col_from, col_to - col_from ) ]
  else
    pre_head = @lines[ row_from ][ 0...col_from ]
    post_tail = @lines[ row_to ][ col_to.. ]
    head = @lines[ row_from ].slice!( col_from..-1 )
    tail = @lines[ row_to ].slice!( 0...col_to )
    retval = [ head ] + @lines.slice!( row_from + 1, row_to - row_from ) + [ tail ]
    @lines[ row_from ] = pre_head + post_tail
  end
  set_modified modified_from_line: row_from
  retval
end

#delete_lineObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/diakonos/buffer/delete.rb', line 29

def delete_line
  remove_selection( DONT_DISPLAY )  if selection_mark

  row = @last_row
  take_snapshot
  retval = nil
  if @lines.length == 1
    retval = @lines[ 0 ]
    @lines[ 0 ] = ""
  else
    retval = @lines[ row ]
    @lines.delete_at row
  end
  cursor_to( row, 0 )
  set_modified modified_from_line: row

  retval
end

#delete_selection(do_display = DO_DISPLAY) ⇒ Object



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
# File 'lib/diakonos/buffer/selection.rb', line 281

def delete_selection( do_display = DO_DISPLAY )
  return  if @text_marks[ :selection ].nil?

  take_snapshot

  selection  = @text_marks[ :selection ]
  start_row  = selection.start_row
  start_col  = selection.start_col
  end_row    = selection.end_row
  end_col    = selection.end_col
  start_line = @lines[ start_row ]

  if end_row == selection.start_row
    @lines[ start_row ] = start_line[ 0...start_col ] + start_line[ end_col.. ]
  else
    case @selection_mode
    when :normal
      end_line = @lines[ end_row ]
      @lines[ start_row ] = start_line[ 0...start_col ] + end_line[ end_col.. ]
      @lines = @lines[ 0..start_row ] + @lines[ (end_row + 1).. ]
    when :block
      @lines[ start_row..end_row ] = @lines[ start_row..end_row ].collect { |line|
        line[ 0...start_col ] + ( line[ end_col.. ] || '' )
      }
    end
  end

  cursor_to start_row, start_col
  remove_selection DONT_DISPLAY
  set_modified do_display, modified_from_line: start_row
end

#delete_to(char) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/diakonos/buffer/delete.rb', line 84

def delete_to( char )
  remove_selection( DONT_DISPLAY )  if selection_mark

  first_row = row = @last_row
  index = @lines[ @last_row ].index( char, @last_col+1 )

  while row < @lines.length - 1 && index.nil?
    row += 1
    index = @lines[ row ].index( char )
  end

  if index
    delete_from_to( first_row, @last_col, row, index )
  end
end

#delete_to_and_from(char, inclusive = NOT_INCLUSIVE) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
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
165
166
# File 'lib/diakonos/buffer/delete.rb', line 118

def delete_to_and_from( char, inclusive = NOT_INCLUSIVE )
  remove_selection( DONT_DISPLAY )  if selection_mark

  start_char = end_char = char
  case char
  when '('
    end_char = ')'
  when '{'
    end_char = '}'
  when '['
    end_char = ']'
  when '<'
    end_char = '>'
  when ')'
    end_char = '('
  when '}'
    end_char = '{'
  when ']'
    end_char = '['
  when '>'
    end_char = '<'
  end

  row = @last_row
  start_index = @lines[ @last_row ].rindex( start_char, @last_col )
  while row > 0 && start_index.nil?
    row -= 1
    start_index = @lines[ row ].rindex( start_char )
  end
  start_row = row

  row = @last_row
  end_index = @lines[ row ].index( end_char, @last_col+1 )
  while row < @lines.length - 1 && end_index.nil?
    row += 1
    end_index = @lines[ row ].index( end_char )
  end
  end_row = row

  if start_index && end_index
    if inclusive
      end_index += 1
    else
      start_index += 1
    end
    cursor_to( start_row, start_index )
    delete_from_to( start_row, start_index, end_row, end_index )
  end
end

#delete_to_eolObject



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/diakonos/buffer/delete.rb', line 48

def delete_to_eol
  remove_selection( DONT_DISPLAY )  if selection_mark

  row = @last_row
  col = @last_col

  take_snapshot
  if @settings[ 'delete_newline_on_delete_to_eol' ] && col == @lines[ row ].size
    next_line = @lines.delete_at( row + 1 )
    @lines[ row ] << next_line
    retval = [ "\n" ]
  else
    retval = [ @lines[ row ][ col.. ] ]
    @lines[ row ] = @lines[ row ][ 0...col ]
  end
  set_modified modified_from_line: row

  retval
end

#diagnostics_for_current_lineObject



483
484
485
# File 'lib/diakonos/buffer.rb', line 483

def diagnostics_for_current_line
  diagnostics_for_line(line: @last_row)
end

#diagnostics_for_line(line:) ⇒ Object



487
488
489
490
491
492
493
# File 'lib/diakonos/buffer.rb', line 487

def diagnostics_for_line(line:)
  if @lsp_session && lsp_uri
    @lsp_session.diagnostics_for_line(uri: lsp_uri, line:)
  else
    []
  end
end

#displayObject



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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/diakonos/buffer/display.rb', line 293

def display
  @pen_down = false

  # Ensure highlight cache is valid up to @top_line - 1
  if @top_line > 0
    if @highlight_cache_valid_to < @top_line - 1
      rebuild_start = @highlight_cache_valid_to + 1
      restore_highlight_state(
        @highlight_cache_valid_to >= 0 ? @highlight_cache[@highlight_cache_valid_to] : nil
      )
      (rebuild_start..(@top_line - 1)).each do |i|
        print_line @lines[i].expand_tabs( @tab_size )
        @highlight_cache[i] = snapshot_highlight_state
      end
      @highlight_cache_valid_to = @top_line - 1
    end

    restore_highlight_state @highlight_cache[@top_line - 1]
  else
    restore_highlight_state nil
  end

  @pen_down = true

  # Draw each on-screen line.
  y = 0
  @lines[ @top_line...($diakonos.main_window_height + @top_line) ].each_with_index do |line, row|
    if @win_line_numbers
      @win_line_numbers.setpos( y, 0 )
      @win_line_numbers.attrset @settings[ 'view.line_numbers.format' ]
      n = ( @top_line+row+1 ).to_s
      @win_line_numbers.addstr(
        @settings[ 'view.line_numbers.number_format' ] % [
          n[ -[ @settings[ 'view.line_numbers.width' ], n.length ].min.. ],
        ]
      )
      line_index = @top_line + row
      if diagnostics_for_line(line: line_index).any?
        @win_line_numbers.addstr( @settings[ 'view.line_numbers.diagnostic_marker' ] || '' )
      else
        @win_line_numbers.addstr( ' ' )
      end
    end
    @win_main.setpos( y, 0 )
    print_line line.expand_tabs( @tab_size )
    line_index = @top_line + row
    @highlight_cache[line_index] = snapshot_highlight_state
    if line_index > @highlight_cache_valid_to
      @highlight_cache_valid_to = line_index
    end
    @win_main.setpos( y, 0 )
    paint_marks line_index
    y += 1
  end

  # Paint the empty space below the file if the file is too short to fit in one screen.
  ( y...$diakonos.main_window_height ).each do |y|
    if @win_line_numbers
      @win_line_numbers.setpos( y, 0 )
      @win_line_numbers.attrset @settings[ 'view.line_numbers.format' ]
      @win_line_numbers.addstr(
        ' ' * (@settings[ 'view.line_numbers.width' ] + COLUMNS_FOR_DIAGNOSTICS)
      )
    end

    @win_main.setpos( y, 0 )
    @win_main.attrset @default_formatting
    linestr = " " * Curses.cols
    if @settings[ "view.nonfilelines.visible" ]
      linestr[ 0 ] = ( @settings[ "view.nonfilelines.character" ] || "~" )
    end

    @win_main.addstr linestr
  end

  paint_column_markers

  @win_line_numbers&.refresh
  @win_main.setpos( @last_screen_y, @last_screen_x )
  @win_main.refresh

  if @language != @original_language
    set_language( @original_language )
  end

  @time_last_viewed = Time.now
end

#file_different?Boolean

Compares MD5 sums of buffer and actual file on disk. Returns true if there is no file on disk.

Returns:

  • (Boolean)


146
147
148
149
150
151
152
153
154
155
156
# File 'lib/diakonos/buffer/file.rb', line 146

def file_different?
  if @name && File.exist?( @name )
    Digest::MD5.hexdigest(
      @lines.join( "\n" )
    ) != Digest::MD5.hexdigest(
      File.read( @name )
    )
  else
    true
  end
end

#file_modified?Boolean

Check if the file which is being edited has been modified since the last time we checked it.

Returns:

  • (Boolean)

    true if file has been modified

  • false if file has not been modified



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/diakonos/buffer/file.rb', line 125

def file_modified?
  modified = false

  if @name
    begin
      mtime = File.mtime( @name )

      if mtime > @last_modification_check
        modified = true
        @last_modification_check = mtime
      end
    rescue Errno::ENOENT
      # Ignore if file doesn't exist
    end
  end

  modified
end

#find(regexps, options = {}) ⇒ Integer

split across newline characters. Once the first element is found, each successive element must match against lines following the first element.

Parameters:

  • regexps (Array)

    Regexps which represent a user-provided regexp,

Returns:

  • (Integer)

    the number of replacements made



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
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
# File 'lib/diakonos/buffer/searching.rb', line 176

def find( regexps, options = {} )
  return  if regexps.nil?

  regexp = regexps[ 0 ]
  return  if regexp.nil? || regexp == //

  direction          = options[ :direction ]
  replacement        = options[ :replacement ]
  auto_choice        = options[ :auto_choice ]
  from_row           = options[ :starting_row ] || @last_row
  from_col           = options[ :starting_col ] || @last_col
  show_context_after = options[ :show_context_after ]

  if options[:starting]
    @num_matches_found = nil
  end

  num_replacements = 0

  # TODO: Just use Range here instead of TextMark?
  search_area = @search_area || TextMark.new( ::Diakonos::Range.new(0, 0, @lines.size - 1, @lines[ -1 ].size), nil )
  if ! search_area.contains?( from_row, from_col )
    from_row, from_col = search_area.start_row, search_area.start_col
  end

  if direction == :opposite
    direction = OPPOSITE_DIRECTION[@last_search_direction]
  end
  @last_search_regexps = regexps
  @last_search_direction = direction

  @wrapped = false

  if direction == :down
    finding = _find_down(regexps, regexp, from_row, from_col, search_area)
  elsif direction == :up
    finding = _find_up(regexps, regexp, search_area, from_row, from_col)
  end

  if ! finding
    remove_selection DONT_DISPLAY
    clear_matches DO_DISPLAY
    if ! options[ :quiet ] && ( replacement.nil? || num_replacements == 0 )
      $diakonos.set_iline "/#{regexp.source}/ not found."
    end
  else
    if @wrapped && ! options[ :quiet ]
      if @search_area
        $diakonos.set_iline( "(search wrapped around to start of search area)" )
      else
        $diakonos.set_iline( "(search wrapped around BOF/EOF)" )
      end
    end

    remove_selection( DONT_DISPLAY )
    @last_finding = finding
    if @settings[ "found_cursor_start" ]
      anchor_selection( finding.end_row, finding.end_col, DONT_DISPLAY )
      cursor_to( finding.start_row, finding.start_col )
    else
      anchor_selection( finding.start_row, finding.start_col, DONT_DISPLAY )
      cursor_to( finding.end_row, finding.end_col )
    end
    if show_context_after
      watermark = Curses.lines / 6
      if @last_row - @top_line > watermark
        pitch_view( @last_row - @top_line - watermark )
      end
    end

    @changing_selection = false

    if regexps.length == 1
      @highlight_regexp = regexp
      highlight_matches
    else
      clear_matches
    end
    display

    if replacement
      # Substitute placeholders (e.g. \1) in str for the group matches of the last match.
      actual_replacement = replacement.dup
      actual_replacement.gsub!( /\\(\\|\d+)/ ) do
        ref = ::Regexp.last_match(1)
        if ref == "\\"
          "\\"
        else
          finding.captured_group(ref.to_i)
        end
      end

      choices = [ CHOICE_YES, CHOICE_NO, CHOICE_ALL, CHOICE_CANCEL, CHOICE_YES_AND_STOP ]
      if @search_area
        choices << CHOICE_WITHIN_SELECTION
      end
      choice = auto_choice || $diakonos.get_choice(
        "#{@num_matches_found} match#{ @num_matches_found != 1 ? 'es' : '' } - Replace this one?",
        choices,
        CHOICE_YES
      )
      case choice
      when CHOICE_YES
        paste [ actual_replacement ]
        num_replacements += 1 + find( regexps, direction:, replacement: )
      when CHOICE_ALL
        num_replacements += replace_all(regexp, replacement)
      when CHOICE_WITHIN_SELECTION
        num_replacements += replace_all(regexp, replacement, within_search_area: true)
      when CHOICE_NO
        num_replacements += find( regexps, direction:, replacement: )
      when CHOICE_CANCEL
        # Do nothing further.
      when CHOICE_YES_AND_STOP
        paste [ actual_replacement ]
        num_replacements += 1
        # Do nothing further.
      end
    end
  end

  num_replacements
end

#find_again(last_search_regexps, direction = @last_search_direction) ⇒ Object



457
458
459
460
461
462
463
464
# File 'lib/diakonos/buffer/searching.rb', line 457

def find_again( last_search_regexps, direction = @last_search_direction )
  if @last_search_regexps.nil? || @last_search_regexps.empty?
    @last_search_regexps = last_search_regexps
  end
  if @last_search_regexps
    find(@last_search_regexps, direction:)
  end
end

#find_opening_match(line, bos_allowed: true, match_close: true) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
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
# File 'lib/diakonos/buffer/display.rb', line 8

def find_opening_match(line, bos_allowed: true, match_close: true)
  open_index = line.length
  open_token_class = nil
  open_match_text = nil
  match = nil
  match_text = nil

  @token_regexps.each do |token_class, regexp|
    match = regexp.match(line)

    if match
      if match.length > 1
        index = match.begin 1
        match_text = match[ 1 ]
        whole_match_index = match.begin 0
      else
        whole_match_index = index = match.begin( 0 )
        match_text = match[ 0 ]
      end

      bos_condition = (
        ! regexp.uses_bos || (
          bos_allowed && (whole_match_index == 0)
        )
      )
      index_is_earlier = (index < open_index)
      can_be_closed = (! match_close || @close_token_regexps[token_class])

      if(
        bos_condition &&
        index_is_earlier &&
        can_be_closed
      )
        open_index = index
        open_token_class = token_class
        open_match_text = match_text
      end
    end
  end

  [open_index, open_token_class, open_match_text]
end

#forgotten?Boolean

Returns:

  • (Boolean)


438
439
440
441
442
443
444
# File 'lib/diakonos/buffer.rb', line 438

def forgotten?
  @settings['close_forgotten_buffers_after'] &&
  @time_last_viewed < (
    Time.now -
    @settings['close_forgotten_buffers_after'] * SECONDS_PER_DAY
  )
end

#go_block_innerObject



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/diakonos/buffer/cursor.rb', line 208

def go_block_inner
  initial_level = indentation_level( @last_row )
  new_row = @lines.length
  ( @last_row...@lines.length ).each do |row|
    next  if @lines[ row ].strip.empty?

    level = indentation_level( row )
    if level > initial_level
      new_row = row
      break
    elsif level < initial_level
      new_row = @last_row
      break
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end

#go_block_nextObject



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/diakonos/buffer/cursor.rb', line 226

def go_block_next
  initial_level = indentation_level( @last_row )
  new_row = @last_row
  passed = false
  ( @last_row+1...@lines.length ).each do |row|
    next  if @lines[ row ].strip.empty?

    level = indentation_level( row )
    if ! passed
      if level < initial_level
        passed = true
      end
    elsif level == initial_level
      new_row = row
      break
    elsif level < initial_level - 1
      break
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end

#go_block_outerObject



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/diakonos/buffer/cursor.rb', line 186

def go_block_outer
  initial_level = indentation_level( @last_row )
  new_row = @last_row
  passed = false
  new_level = initial_level
  ( 0...@last_row ).reverse_each do |row|
    next  if @lines[ row ].strip.empty?

    level = indentation_level( row )
    if ! passed
      passed = ( level < initial_level )
      new_level = level
    elsif level < new_level
      new_row = ( row+1..@last_row ).find { |r|
        ! @lines[ r ].strip.empty?
      }
      break
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end

#go_block_previousObject



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/diakonos/buffer/cursor.rb', line 248

def go_block_previous
  initial_level = indentation_level( @last_row )
  new_row = @last_row
  passed = false   # search for unindent
  passed2 = false  # search for reindent
  ( 0...@last_row ).reverse_each do |row|
    next  if @lines[ row ].strip.empty?

    level = indentation_level( row )
    if ! passed
      if level < initial_level
        passed = true
      end
    elsif ! passed2
      if level >= initial_level
        new_row = row
        passed2 = true
      elsif level <= initial_level - 2
        # No previous block
        break
      end
    elsif level < initial_level
      new_row = (row+1..@last_row).find { |r|
        ! @lines[ r ].strip.empty?
      }
      break
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end

#go_to_char(char, after = ON_CHAR) ⇒ Object



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

def go_to_char( char, after = ON_CHAR )
  r = @last_row
  i = @lines[ r ].index( char, @last_col + 1 )
  if i
    if after
      i += 1
    end
    return cursor_to r, i, DO_DISPLAY
  end

  loop do
    r += 1
    break  if r >= @lines.size

    i = @lines[ r ].index( char )
    if i
      if after
        i += 1
      end
      return cursor_to r, i, DO_DISPLAY
    end
  end
end

#go_to_char_previous(char, after = ON_CHAR) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/diakonos/buffer/cursor.rb', line 303

def go_to_char_previous( char, after = ON_CHAR )
  r = @last_row
  search_from = @last_col - 1
  if search_from >= 0
    i = @lines[ r ].rindex( char, search_from )
    if i
      if after
        i += 1
      end
      return cursor_to r, i, DO_DISPLAY
    end
  end

  loop do
    r -= 1
    break  if r < 0

    i = @lines[ r ].rindex( char )
    if i
      if after
        i += 1
      end
      return cursor_to r, i, DO_DISPLAY
    end
  end
end

#go_to_line(line = nil, column = nil, do_display = DO_DISPLAY) ⇒ Object



182
183
184
# File 'lib/diakonos/buffer/cursor.rb', line 182

def go_to_line( line = nil, column = nil, do_display = DO_DISPLAY )
  cursor_to( line || @last_row, column || 0, do_display )
end

#go_to_next_bookmarkObject



5
6
7
8
9
10
11
12
13
# File 'lib/diakonos/buffer/bookmarking.rb', line 5

def go_to_next_bookmark
  cur_pos = Bookmark.new( self, @last_row, @last_col )
  next_bm = @bookmarks.find { |bm|
    bm > cur_pos
  }
  if next_bm
    cursor_to( next_bm.row, next_bm.col, DO_DISPLAY )
  end
end

#go_to_pair_matchObject



433
434
435
436
437
438
439
# File 'lib/diakonos/buffer/searching.rb', line 433

def go_to_pair_match
  row, col = pos_of_pair_match
  if row && col && cursor_to(row, col)
    highlight_pair
    display
  end
end

#go_to_previous_bookmarkObject



15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/diakonos/buffer/bookmarking.rb', line 15

def go_to_previous_bookmark
  cur_pos = Bookmark.new( self, @last_row, @last_col )
  # There's no reverse_find method, so, we have to do this manually.
  prev = nil
  @bookmarks.reverse_each do |bm|
    if bm < cur_pos
      prev = bm
      break
    end
  end
  if prev
    cursor_to( prev.row, prev.col, DO_DISPLAY )
  end
end

#grep(regexp_source) ⇒ Object

Returns an Array of results, where each result is a String usually containing n‘s due to context



534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
# File 'lib/diakonos/buffer/searching.rb', line 534

def grep( regexp_source )
  if @name
    filename = File.basename( @name )
    filepath = @name
  else
    filename = "(unnamed buffer)"
    filepath = "(unnamed buffer #{object_id})"
  end

  ::Diakonos.grep_array(
    Regexp.new( regexp_source ),
    @lines,
    $diakonos.settings[ 'grep.context' ],
    "#{filename}:",
    filepath
  )
end

#highlight_matches(regexp = @highlight_regexp) ⇒ Object



335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/diakonos/buffer/searching.rb', line 335

def highlight_matches( regexp = @highlight_regexp )
  @highlight_regexp = regexp
  return  if @highlight_regexp.nil?

  if @search_area
    lines = self.search_area_lines
    line_index_offset = @search_area.start_row
    col_offset = @search_area.start_col
  else
    lines = @lines
    line_index_offset = 0
    col_offset = 0
  end

  grepped_lines = lines.grep_indices( @highlight_regexp )
  grepped_lines.count
  found_marks = grepped_lines.collect { |line_index, start_col, end_col|
    TextMark.new(
      ::Diakonos::Range.new(
        line_index + line_index_offset,
        start_col + ( line_index == 0 ? col_offset : 0 ),
        line_index + line_index_offset,
        end_col,
      ),
      @settings["lang.#{@language}.format.found"]
    )
  }
  @text_marks[:found] = found_marks
  @num_matches_found ||= found_marks.size
end

#highlight_pairObject



441
442
443
444
445
446
447
448
449
450
451
# File 'lib/diakonos/buffer/searching.rb', line 441

def highlight_pair
  match_row, match_col = pos_of_pair_match( @last_row, @last_col )
  if match_col.nil?
    @text_marks[ :pair ] = nil
  else
    @text_marks[ :pair ] = TextMark.new(
      ::Diakonos::Range.new(match_row, match_col, match_row, match_col + 1),
      @settings[ "lang.#{@language}.format.pair" ] || @settings[ "lang.shared.format.pair" ]
    )
  end
end

#in_line(x, y) ⇒ Object

Returns true iff the given column, x, is less than the length of the given line, y.



447
448
449
# File 'lib/diakonos/buffer.rb', line 447

def in_line( x, y )
  x + @left_column < line_at( y ).length
end

#indent(row = @last_row, do_display = DO_DISPLAY) ⇒ Object



166
167
168
169
# File 'lib/diakonos/buffer/indentation.rb', line 166

def indent( row = @last_row, do_display = DO_DISPLAY )
  level = indentation_level( row, DONT_USE_INDENT_IGNORE )
  set_indent  row, level + 1, do_display:
end

#indentation_level(row, use_indent_ignore = USE_INDENT_IGNORE) ⇒ Object



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
# File 'lib/diakonos/buffer/indentation.rb', line 51

def indentation_level( row, use_indent_ignore = USE_INDENT_IGNORE )
  line = @lines[ row ]

  if use_indent_ignore
    if line =~ /^[\s#{@indent_ignore_charset}]*$/ || line == ""
      level = 0
    elsif line =~ /^([\s#{@indent_ignore_charset}]+)[^\s#{@indent_ignore_charset}]/
      whitespace = ::Regexp.last_match(1).expand_tabs( @tab_size )
      level = whitespace.length / @indent_size
      if @indent_roundup && ( whitespace.length % @indent_size > 0 )
        level += 1
      end
    else
      level = 0
    end
  else
    level = 0
    if line =~ /^([\s]+)/
      whitespace = ::Regexp.last_match(1).expand_tabs( @tab_size )
      level = whitespace.length / @indent_size
      if @indent_roundup && ( whitespace.length % @indent_size > 0 )
        level += 1
      end
    end
  end

  level
end

#insert_char(c) ⇒ Object



208
209
210
211
212
213
214
215
# File 'lib/diakonos/buffer.rb', line 208

def insert_char( c )
  row = @last_row
  col = @last_col
  take_snapshot(typing: true)
  line = @lines[ row ]
  @lines[ row ] = line[ 0...col ] + c.chr + line[ col.. ]
  set_modified modified_from_line: row
end

#insert_string(str) ⇒ Object



217
218
219
220
221
222
223
224
# File 'lib/diakonos/buffer.rb', line 217

def insert_string( str )
  row = @last_row
  col = @last_col
  take_snapshot(typing: true)
  line = @lines[ row ]
  @lines[ row ] = line[ 0...col ] + str + line[ col.. ]
  set_modified modified_from_line: row
end

#join_lines(row = @last_row, strip = DONT_STRIP_LINE) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
# File 'lib/diakonos/buffer.rb', line 267

def join_lines( row = @last_row, strip = DONT_STRIP_LINE )
  take_snapshot(typing: true)
  next_line = @lines.delete_at( row + 1 )
  return false  if next_line.nil?

  if strip
    next_line = ' ' + next_line.strip
  end
  @lines[ row ] << next_line
  set_modified modified_from_line: row
end

#join_lines_upward(row = @last_row, strip = DONT_STRIP_LINE) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/diakonos/buffer.rb', line 240

def join_lines_upward( row = @last_row, strip = DONT_STRIP_LINE )
  return false  if row == 0

  take_snapshot

  line       = @lines.delete_at( row )
  old_line   = @lines[ row-1 ]

  new_x_pos  = old_line.length

  if strip
    line.strip!

    # Only prepend a space if the line above isn't empty.
    if ! old_line.strip.empty?
      line = ' ' + line
      new_x_pos += 1
    end
  end

  @lines[ row-1 ] << line

  cursor_to( row-1, new_x_pos )

  set_modified modified_from_line: row - 1
end

#lengthObject



184
185
186
# File 'lib/diakonos/buffer.rb', line 184

def length
  @lines.length
end

#line_at(y) ⇒ Object



423
424
425
426
427
428
429
430
# File 'lib/diakonos/buffer.rb', line 423

def line_at( y )
  row = @top_line + y
  if row < 0
    nil
  else
    @lines[ row ]
  end
end

#lsp_uriObject



499
500
501
502
503
# File 'lib/diakonos/buffer.rb', line 499

def lsp_uri
  if name
    "#{Lsp::FILE_URI_PREFIX}#{name}"
  end
end

#modified?Boolean

Returns:

  • (Boolean)


192
193
194
# File 'lib/diakonos/buffer.rb', line 192

def modified?
  @modified
end

#nearest_basis_row_from(starting_row, next_line_check: true) ⇒ Integer

Parameters:

  • starting_row (Integer)
  • next_line_check (Boolean) (defaults to: true)

Returns:

  • (Integer)


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
# File 'lib/diakonos/buffer/indentation.rb', line 83

def nearest_basis_row_from(starting_row, next_line_check: true)
  row = starting_row-1

  if @lines[row] =~ @indenters_next_line || @lines[row] =~ @indenters
    return row
  end

  loop do
    return nil  if row.nil? || row < 0

    while (
      @lines[row] =~ /^[\s#{@indent_ignore_charset}]*$/ ||
      @lines[row] =~ @settings[ "lang.#{@language}.indent.ignore" ] ||
      @lines[row] =~ @settings[ "lang.#{@language}.indent.not_indented" ]
    )
      row = nearest_basis_row_from(row)
      return nil  if row.nil?
    end

    if next_line_check
      row_before = nearest_basis_row_from(row, next_line_check: false)
      if row_before && @lines[row_before] =~ @indenters_next_line
        row = row_before
        next
      end
    end

    break
  end

  row
end

#nice_nameObject



196
197
198
# File 'lib/diakonos/buffer.rb', line 196

def nice_name
  @name || @settings[ "status.unnamed_str" ]
end

#paint_column_markersObject



159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/diakonos/buffer/display.rb', line 159

def paint_column_markers
  $diakonos.column_markers.each_value do |data|
    column = data[ :column ]
    next  if column.nil?
    next  if column > Curses.cols - @left_column || column - @left_column < 0

    num_lines_to_paint = [ $diakonos.main_window_height, @lines.size - @top_line ].min
    ( 0...num_lines_to_paint ).each do |row|
      @win_main.setpos( row, column - @left_column )
      @win_main.attrset data[ :format ]
      @win_main.addstr( @lines[ @top_line + row ][ column + @left_column ] || ' ' )
    end
  end
end

#paint_marks(row) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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
# File 'lib/diakonos/buffer/display.rb', line 114

def paint_marks( row )
  string = @lines[ row ][ @left_column...@left_column + Curses.cols ]
  return  if string.nil? || string == ""

  string = string.expand_tabs( @tab_size )
  cury = @win_main.cury
  curx = @win_main.curx

  @text_marks.values.flatten.reverse_each do |text_mark|
    next  if text_mark.nil?

    @win_main.attrset text_mark.formatting

    case @selection_mode
    when :normal
      if ( (text_mark.start_row + 1)..(text_mark.end_row - 1) ) === row
        @win_main.setpos( cury, curx )
        @win_main.addstr string
      elsif row == text_mark.start_row && row == text_mark.end_row
        paint_single_row_mark( row, text_mark, string, curx, cury )
      elsif row == text_mark.start_row
        expanded_col = tab_expanded_column( text_mark.start_col, row )
        if expanded_col < @left_column + Curses.cols
          left = [ expanded_col - @left_column, 0 ].max
          @win_main.setpos( cury, curx + left )
          @win_main.addstr string[ left.. ]
        end
      elsif row == text_mark.end_row
        right = tab_expanded_column( text_mark.end_col, row ) - @left_column
        @win_main.setpos( cury, curx )
        @win_main.addstr string[ 0...right ]
      else
        # This row not in selection.
      end
    when :block
      if (
        text_mark.start_row <= row && row <= text_mark.end_row ||
        text_mark.end_row <= row && row <= text_mark.start_row
      )
        paint_single_row_mark( row, text_mark, string, curx, cury )
      end
    end
  end
end

#paint_single_row_mark(row, text_mark, string, curx, cury) ⇒ Object

Worker function for painting only part of a row.



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/diakonos/buffer/display.rb', line 102

def paint_single_row_mark( row, text_mark, string, curx, cury )
  expanded_col = tab_expanded_column( text_mark.start_col, row )
  if expanded_col < @left_column + Curses.cols
    left = [ expanded_col - @left_column, 0 ].max
    right = tab_expanded_column( text_mark.end_col, row ) - @left_column
    if left < right
      @win_main.setpos( cury, curx + left )
      @win_main.addstr string[ left...right ]
    end
  end
end

#pan_view(x = 1, do_display = DO_DISPLAY) ⇒ Object

Returns the amount the view was actually panned.



512
513
514
515
516
# File 'lib/diakonos/buffer.rb', line 512

def pan_view( x = 1, do_display = DO_DISPLAY )
  old_left_column = @left_column
  pan_view_to( @left_column + x, do_display )
  @left_column - old_left_column
end

#pan_view_to(left_column, do_display = DO_DISPLAY) ⇒ Object



505
506
507
508
509
# File 'lib/diakonos/buffer.rb', line 505

def pan_view_to( left_column, do_display = DO_DISPLAY )
  @left_column = [ left_column, 0 ].max
  record_mark_start_and_end
  display  if do_display
end

#paragraph_under_cursorObject

Returns an array of lines of the current paragraph.



703
704
705
706
# File 'lib/diakonos/buffer.rb', line 703

def paragraph_under_cursor
  ( first, _ ), ( last, _ ) = paragraph_under_cursor_pos
  @lines[ first..last ]
end

#paragraph_under_cursor_posObject

Returns the coordinates of the first and last line of the current paragraph.



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
736
737
738
739
740
741
# File 'lib/diakonos/buffer.rb', line 710

def paragraph_under_cursor_pos
  if @lines[ @last_row ] =~ /^\s*$/
    return [
      [ @last_row, 0 ],
      [ @last_row, @lines[ @last_row ].length - 1 ],
    ]
  end

  upper_boundary = 0
  lower_boundary = @lines.size - 1

  @last_row.downto( 0 ) do |i|
    line = @lines[ i ]
    if line =~ /^\s*$/
      upper_boundary = i + 1
      break
    end
  end

  @last_row.upto( @lines.size - 1 ) do |i|
    line = @lines[ i ]
    if line =~ /^\s*$/
      lower_boundary = i - 1
      break
    end
  end

  [
    [ upper_boundary, 0 ],
    [ lower_boundary, @lines[ lower_boundary ].length - 1 ],
  ]
end

#parsed_indent(opts = {}) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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/diakonos/buffer/indentation.rb', line 116

def parsed_indent( opts = {} )
  row        = opts.fetch( :row,        @last_row )
  do_display = opts.fetch( :do_display, true )
  undoable   = opts.fetch( :undoable,   true )
  cursor_eol = opts.fetch( :cursor_eol, false )

  if row == 0 || @lines[ row ] =~ @settings[ "lang.#{@language}.indent.not_indented" ]
    level = 0
  else
    basis_row = nearest_basis_row_from(row)

    if basis_row.nil?
      level = 0
    else
      # @lines[basis_row] += " // x"
      level = indentation_level(basis_row)

      prev_line = @lines[basis_row]
      line = @lines[row]

      if @preventers
        prev_line = prev_line.gsub( @preventers, "" )
        line = line.gsub( @preventers, "" )
      end

      nl_indenter_index = (prev_line =~ @indenters_next_line)

      if nl_indenter_index && basis_row == row-1
        level += 1
      elsif prev_line =~ @indenters
        last_indenter = last_match_index(prev_line, @indenters)
        last_unindenter = last_match_index(prev_line, @unindenters)
        if last_unindenter.nil? || last_indenter >= last_unindenter
          level += 1
        end
      end

      unindenter_index = (line =~ @unindenters)
      if unindenter_index
        indenter_index = (line =~ @indenters)
        if indenter_index.nil? || unindenter_index <= indenter_index
          level -= 1
        end
      end
    end
  end

  set_indent  row, level, do_display:, undoable:, cursor_eol:
end

#paste(text, do_parsed_indent: false, typing: false) ⇒ Object

text is an array of Strings, or a String with zero or more newlines (“n”)



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/diakonos/buffer/selection.rb', line 314

def paste( text, do_parsed_indent: false, typing: false )
  return  if text.nil?

  if ! text.kind_of?(Array)
    s = text.to_s
    if s.include?( "\n" )
      text = s.split( "\n", -1 )
    else
      text = [ s ]
    end
  end

  take_snapshot(typing:)

  delete_selection DONT_DISPLAY

  row = @last_row
  col = @last_col
  new_col = nil
  line = @lines[ row ]
  if text.length == 1
    @lines[ row ] = line[ 0...col ] + text[ 0 ] + line[ col.. ]
    if do_parsed_indent
      parsed_indent  row:, do_display: false
    end
    cursor_to( @last_row, @last_col + text[ 0 ].length, DONT_DISPLAY, ! typing )
  elsif text.length > 1

    case @selection_mode
    when :normal
      @lines[ row ] = line[ 0...col ] + text[ 0 ]
      @lines[ row + 1, 0 ] = text[ -1 ] + line[ col.. ]
      @lines[ row + 1, 0 ] = text[ 1..-2 ]
      new_col = column_of( text[ -1 ].length )
    when :block
      @lines += [ '' ] * [ 0, ( row + text.length - @lines.length ) ].max
      range = row...( row + text.length )
      @lines[range] = (
        @lines[range]
        .collect
        .with_index { |line,index|
          pre = line[0...col].ljust(col)
          post = line[col..]

          "#{pre}#{text[ index ]}#{post}"
        }
      )
      new_col = col + text[-1].length
    end

    new_row = @last_row + text.length - 1
    if do_parsed_indent
      ( row..new_row ).each do |r|
        parsed_indent  row: r, do_display: false
      end
    end
    cursor_to( new_row, new_col )

  end

  set_modified modified_from_line: row
end

#pitch_view(y = 1, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY) ⇒ Object

Returns the amount the view was actually pitched.



590
591
592
# File 'lib/diakonos/buffer.rb', line 590

def pitch_view( y = 1, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY )
  pitch_view_to( @top_line + y, do_pitch_cursor, do_display )
end

#pitch_view_to(new_top_line, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY) ⇒ Object



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
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
# File 'lib/diakonos/buffer.rb', line 518

def pitch_view_to( new_top_line, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY )
  old_top_line = @top_line

  if new_top_line < 0
    @top_line = 0
  elsif new_top_line + $diakonos.main_window_height > @lines.length
    @top_line = [ @lines.length - $diakonos.main_window_height, 0 ].max
  else
    @top_line = new_top_line
  end

  old_row = @last_row
  old_col = @last_col

  changed = ( @top_line - old_top_line )
  if changed != 0 && do_pitch_cursor
    @last_row += changed
  end

  height = [ $diakonos.main_window_height, @lines.length ].min

  @last_row = NumberFitter.fit(
    number: @last_row,
    min: @top_line,
    max: @top_line + height - 1,
  )

  if @last_row - @top_line < @settings["view.margin.y"]
    @last_row = NumberFitter.fit(
      number: @top_line + @settings["view.margin.y"],
      min: @top_line,
      max: @top_line + height - 1,
    )
  elsif @top_line + height - 1 - @last_row < @settings[ "view.margin.y" ]
    @last_row = NumberFitter.fit(
      number: @top_line + height - 1 - @settings[ "view.margin.y" ],
      min: @top_line,
      max: @top_line + height - 1,
    )
  end

  @last_col = NumberFitter.fit(
    number: @last_col,
    min: @left_column,
    max: [
      @left_column + Curses.cols - 1,
      @lines[@last_row].length,
    ].min
  )

  @last_screen_y = @last_row - @top_line
  @last_screen_x = tab_expanded_column( @last_col, @last_row ) - @left_column

  record_mark_start_and_end

  if changed != 0
    if ! @auto_anchored && ! @changing_selection && selecting?
      remove_selection DONT_DISPLAY
    end

    highlight_matches
    if $diakonos.there_was_non_movement
      $diakonos.push_cursor_state( old_top_line, old_row, old_col )
    end
  end

  display  if do_display

  changed
end

#pos_of_next(regexp, start_row, start_col) ⇒ Object



372
373
374
375
376
377
378
379
380
381
382
# File 'lib/diakonos/buffer/searching.rb', line 372

def pos_of_next( regexp, start_row, start_col )
  row, col = start_row, start_col
  col = @lines[ row ].index( regexp, col )
  while col.nil? && row < @lines.length - 1
    row += 1
    col = @lines[ row ].index( regexp )
  end
  if col
    [ row, col, Regexp.last_match( 0 ) ]
  end
end

#pos_of_pair_match(row = @last_row, col = @last_col) ⇒ Object



402
403
404
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
# File 'lib/diakonos/buffer/searching.rb', line 402

def pos_of_pair_match( row = @last_row, col = @last_col )
  c = @lines[ row ][ col ]
  data = CHARACTER_PAIRS[ c ]
  return  if data.nil?

  d = data[ :partner ]
  c_ = Regexp.escape c
  d_ = Regexp.escape d
  target = /(?:#{c_}|#{d_})/

  case data[ :direction ]
  when :forward
    row, col, char = pos_of_next( target, row, col + 1 )
    while char == c  # Take care of nested pairs
      row, col = pos_of_pair_match( row, col )
      break  if col.nil?

      row, col, char = pos_of_next( target, row, col + 1 )
    end
  when :backward
    row, col, char = pos_of_prev( target, row, col - 1 )
    while char == c  # Take care of nested pairs
      row, col = pos_of_pair_match( row, col )
      break  if col.nil?

      row, col, char = pos_of_prev( target, row, col - 1 )
    end
  end
  [ row, col ]
end

#pos_of_prev(regexp, start_row, start_col) ⇒ Object



384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/diakonos/buffer/searching.rb', line 384

def pos_of_prev( regexp, start_row, start_col )
  row, col = start_row, start_col
  if col < 0
    row -= 1
    col = -1
  end
  return  if row < 0

  col = @lines[ row ].rindex( regexp, col )
  while col.nil? && row > 0
    row -= 1
    col = @lines[ row ].rindex( regexp )
  end
  if col
    [ row, col, Regexp.last_match( 0 ) ]
  end
end

This method assumes that the cursor has been set up already at the left-most column of the correct on-screen row. It merely unintelligently prints the characters on the current curses line, refusing to print characters of the in-buffer line which are offscreen.



186
187
188
189
190
191
192
193
194
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/diakonos/buffer/display.rb', line 186

def print_line( line )
  i = 0
  substr = nil

  while i < line.length
    substr = line[ i.. ]
    if @continued_format_class
      close_index, close_match_text = find_closing_match(
        substr,
        @close_token_regexps[@continued_format_class],
        bos_allowed: i == 0
      )

      if close_match_text.nil?
        print_string truncate_off_screen( substr, i )
        print_padding_from( line.length )
        i = line.length
      else
        end_index = close_index + close_match_text.length
        print_string truncate_off_screen( substr[ 0...end_index ], i )
        @continued_format_class = nil
        i += end_index
      end
    else
      first_index, first_token_class, first_word = find_opening_match(
        substr,
        bos_allowed: i == 0,
        match_close: false
      )

      if @lang_stack.any?
        prev_lang, close_token_class = @lang_stack[-1]
        close_index, close_match_text = find_closing_match(
          substr,
          $diakonos.close_token_regexps[prev_lang][close_token_class],
          bos_allowed: i == 0
        )

        if close_match_text && close_index <= first_index
          # Print any remaining text in the embedded language
          s = substr[0...close_index]
          print_string( truncate_off_screen(s, i) )
          i += s.length

          @lang_stack.pop
          set_language prev_lang

          print_string(
            truncate_off_screen(
              substr[
                close_index...(close_index + close_match_text.length)
              ],
              i
            ),
            @token_formats[close_token_class]
          )
          i += close_match_text.length

          # Continue printing from here.
          next
        end
      end

      if first_word
        if first_index > 0
          # Print any preceding text in the default format
          print_string truncate_off_screen( substr[ 0...first_index ], i )
          i += substr[ 0...first_index ].length
        end

        print_string( truncate_off_screen( first_word, i ), @token_formats[ first_token_class ] )
        i += first_word.length

        if @close_token_regexps[first_token_class]
          change_to = @settings["lang.#{@language}.tokens.#{first_token_class}.change_to"]
          if change_to
            @lang_stack.push [ @language, first_token_class ]
            set_language change_to
          else
            @continued_format_class = first_token_class
          end
        end
      else
        print_string truncate_off_screen( substr, i )
        i += substr.length
        break
      end
    end
  end

  print_padding_from i
end


279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/diakonos/buffer/display.rb', line 279

def print_padding_from( col )
  return  if ! @pen_down

  if col < @left_column
    remainder = Curses.cols
  else
    remainder = @left_column + Curses.cols - col
  end

  if remainder > 0
    print_string( " " * remainder )
  end
end


174
175
176
177
178
179
180
# File 'lib/diakonos/buffer/display.rb', line 174

def print_string( string, formatting = ( @token_formats[ @continued_format_class ] || @default_formatting ) )
  return  if ! @pen_down
  return  if string.nil?

  @win_main.attrset formatting
  @win_main.addstr string
end

#record_mark_start_and_endObject

@mark_start[ “col” ] is inclusive, @mark_end[ “col” ] is exclusive.



7
8
9
10
11
12
13
14
15
16
17
18
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
# File 'lib/diakonos/buffer/selection.rb', line 7

def record_mark_start_and_end
  if @mark_anchor.nil?
    @text_marks[ :selection ] = nil
    return
  end

  crow = @last_row
  ccol = @last_col
  arow = @mark_anchor[ 'row' ]
  acol = @mark_anchor[ 'col' ]

  case @selection_mode
  when :normal
    anchor_first = true

    if crow < arow
      anchor_first = false
    elsif crow > arow
      anchor_first = true
    elsif ccol < acol
      anchor_first = false
    end

    if anchor_first
      @text_marks[ :selection ] = TextMark.new(
        ::Diakonos::Range.new(arow, acol, crow, ccol), @selection_formatting
      )
    else
      @text_marks[ :selection ] = TextMark.new(
        ::Diakonos::Range.new(crow, ccol, arow, acol), @selection_formatting
      )
    end
  when :block
    if crow < arow
      if ccol < acol # Northwest
        @text_marks[ :selection ] = TextMark.new(
          ::Diakonos::Range.new(crow, ccol, arow, acol), @selection_formatting
        )
      else           # Northeast
        @text_marks[ :selection ] = TextMark.new(
          ::Diakonos::Range.new(crow, acol, arow, ccol), @selection_formatting
        )
      end
    elsif ccol < acol  # Southwest
      @text_marks[ :selection ] = TextMark.new(
        ::Diakonos::Range.new(arow, ccol, crow, acol), @selection_formatting
      )
    else            # Southeast
      @text_marks[ :selection ] = TextMark.new(
        ::Diakonos::Range.new(arow, acol, crow, ccol), @selection_formatting
      )
    end
  end
end

#remove_selection(do_display = DO_DISPLAY) ⇒ Object



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

def remove_selection( do_display = DO_DISPLAY )
  return  if selection_mark.nil?

  @mark_anchor = nil
  record_mark_start_and_end
  @changing_selection = false
  @last_finding = nil
  display  if do_display
end

#replace_all(regexp, replacement, within_search_area: false) ⇒ Integer

Returns the number of replacements made.

Returns:

  • (Integer)

    the number of replacements made



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/diakonos/buffer/searching.rb', line 301

def replace_all( regexp, replacement, within_search_area: false )
  return  if ( regexp.nil? || replacement.nil? )

  num_replacements = 0

  take_snapshot

  if within_search_area && @search_area
    lines = self.search_area_lines
  else
    lines = @lines
  end

  lines_modified = lines.collect { |line|
    num_replacements += line.scan(regexp).size
    line.gsub( regexp, replacement )
  }

  if within_search_area && @search_area
    @lines[@search_area.start_row][@search_area.start_col..-1] = lines_modified[0]
    if @search_area.end_row - @search_area.start_row > 1
      @lines[@search_area.start_row+1..@search_area.end_row-1] = lines_modified[1..-2]
    end
    @lines[@search_area.end_row][0..@search_area.end_col] = lines_modified[-1]
  else
    @lines = lines_modified
  end

  set_modified modified_from_line: 0
  clear_matches
  display
  num_replacements
end

#replace_char(c) ⇒ Object



200
201
202
203
204
205
206
# File 'lib/diakonos/buffer.rb', line 200

def replace_char( c )
  row = @last_row
  col = @last_col
  take_snapshot(typing: true)
  @lines[ row ][ col ] = c
  set_modified modified_from_line: row
end

#reset_displayObject



139
140
141
142
# File 'lib/diakonos/buffer.rb', line 139

def reset_display
  @win_main = $diakonos.win_main
  @win_line_numbers = $diakonos.win_line_numbers
end

#row_of(y) ⇒ Object

Translates the window row, y, to a buffer-relative row index.



457
458
459
# File 'lib/diakonos/buffer.rb', line 457

def row_of( y )
  @top_line + y
end

#row_to_y(row) ⇒ Object

Returns nil if the row is off-screen.



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

def row_to_y( row )
  return nil if row.nil?

  y = row - @top_line
  y = nil if ( y < 0 ) || ( y > @top_line + $diakonos.main_window_height - 1 )
  y
end

#save(filename = nil, prompt_overwrite = DONT_PROMPT_OVERWRITE) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
# File 'lib/diakonos/buffer/file.rb', line 5

def save( filename = nil, prompt_overwrite = DONT_PROMPT_OVERWRITE )
  if filename
    name = File.expand_path( filename )
  else
    name = @name
  end

  if(
    @read_only &&
    FileTest.exist?(@name) &&
    FileTest.exist?(name) &&
    ( File.stat(@name).ino == File.stat(name).ino )
  )
    $diakonos.set_iline "#{name} cannot be saved since it is read-only."
  else
    @read_only = false
    if name.nil?
      $diakonos.save_file_as
    else
      proceed = true

      if prompt_overwrite && FileTest.exist?( name )
        proceed = false
        choice = $diakonos.get_choice(
          "Overwrite existing '#{name}'?",
          [ CHOICE_YES, CHOICE_NO ],
          CHOICE_NO
        )
        case choice
        when CHOICE_YES
          proceed = true
        when CHOICE_NO
          proceed = false
        end
      end

      if file_modified?
        proceed = ! $diakonos.revert( "File has been altered externally.  Load on-disk version?" )
      end

      if proceed
        save_copy name
        @name = name
        @last_modification_check = File.mtime( @name )
        saved = true

        if @name =~ %r{#{$diakonos.diakonos_home}/.*\.conf}
          $diakonos.load_configuration
          $diakonos.initialize_display
        end

        @modified = false

        display
        $diakonos.update_status_line
      end
    end
  end

  saved
end

#save_copy(filename) ⇒ Object

Returns true on successful write.



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
# File 'lib/diakonos/buffer/file.rb', line 68

def save_copy( filename )
  return false if filename.nil?

  name = File.expand_path( filename )

  if @settings['save_backup_files']
    begin
      FileUtils.cp name, name+'~', preserve: true
    rescue Errno::ENOENT
      # Do nothing if file didn't exist yet
    end
  end

  File.open( name, "w" ) do |f|
    was_modified = false

    @lines[ 0..-2 ].each do |line|
      if @settings[ 'strip_trailing_whitespace_on_save' ]
        stripped = line.rstrip!
        was_modified ||= stripped
      end
      f.puts line
    end

    line = @lines[ -1 ]
    if @settings[ 'strip_trailing_whitespace_on_save' ]
      stripped = line.rstrip!
      was_modified ||= stripped
    end

    if line != ""
      # No final newline character
      if @settings[ "eof_newline" ]
        line << "\n"
        @lines << ''
        was_modified = true
      end
      f.print line
    end

    if (
      @settings['strip_trailing_whitespace_on_save'] &&
      @last_col > @lines[@last_row].size
    )
      cursor_to @last_row, @lines[ @last_row ].size
    end

    if was_modified
      set_modified
    end
  end
end

#search_area?Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/diakonos/buffer/searching.rb', line 23

def search_area?
  @search_area
end

#search_area_linesArray<String>

Returns:



56
57
58
59
60
61
62
63
64
# File 'lib/diakonos/buffer/searching.rb', line 56

def search_area_lines
  return []  if @search_area.nil?

  lines = @lines[@search_area.start_row..@search_area.end_row]
  lines[0] = lines[0][@search_area.start_col..]
  lines[-1] = lines[-1][0..@search_area.end_col]

  lines
end

#seek(regexp, direction = :down) ⇒ Object



466
467
468
469
470
471
472
473
474
475
476
477
478
479
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
522
523
524
525
526
527
528
529
530
# File 'lib/diakonos/buffer/searching.rb', line 466

def seek( regexp, direction = :down )
  return  if regexp.nil? || regexp == //

  found_row = nil
  found_col = nil
  found_text = nil

  catch :found do
    if direction == :down
      # Check the current row first.

      index, match_text = @lines[ @last_row ].group_index( regexp, @last_col + 1 )
      if index
        found_row = @last_row
        found_col = index
        found_text = match_text
        throw :found
      end

      # Check below the cursor.

      ( (@last_row + 1)...@lines.length ).each do |i|
        index, match_text = @lines[ i ].group_index( regexp )
        if index
          found_row = i
          found_col = index
          found_text = match_text
          throw :found
        end
      end

    else
      # Check the current row first.

      col_to_check = @last_col - 1
      if col_to_check >= 0
        index, match_text = @lines[ @last_row ].group_rindex( regexp, col_to_check )
        if index
          found_row = @last_row
          found_col = index
          found_text = match_text
          throw :found
        end
      end

      # Check above the cursor.

      (@last_row - 1).downto( 0 ) do |i|
        index, match_text = @lines[ i ].group_rindex( regexp )
        if index
          found_row = i
          found_col = index
          found_text = match_text
          throw :found
        end
      end
    end
  end

  if found_text
    cursor_to( found_row, found_col )
    display
    true
  end
end

#select(from_regexp, to_regexp, include_ending: true) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/diakonos/buffer/selection.rb', line 156

def select( from_regexp, to_regexp, include_ending: true )
  start_row = nil

  @lines[ 0..@last_row ].reverse.each_with_index do |line,index|
    if line =~ from_regexp
      start_row = @last_row - index
      break
    end
  end
  if start_row
    end_row = nil
    @lines[ start_row.. ].each_with_index do |line,index|
      if line =~ to_regexp
        end_row = start_row + index
        break
      end
    end
    if end_row
      if include_ending
        end_row += 1
      end
      anchor_selection( start_row, 0, DONT_DISPLAY )
      cursor_to( end_row, 0 )
      display
    end
  end
end

#select_allObject



91
92
93
94
95
# File 'lib/diakonos/buffer/selection.rb', line 91

def select_all
  selection_mode_normal
  anchor_selection( 0, 0, DONT_DISPLAY )
  cursor_to( @lines.length - 1, @lines[ -1 ].length, DO_DISPLAY )
end

#select_current_lineObject



70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/diakonos/buffer/selection.rb', line 70

def select_current_line
  selection_mode_normal
  anchor_selection( @last_row, 0, DONT_DISPLAY )
  if @last_row == @lines.size - 1
    row = @last_row
    col = @lines[ @last_row ].size
  else
    row = @last_row + 1
    col = 0
  end
  cursor_to( row, col, DO_DISPLAY )
end

#select_wordObject



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/diakonos/buffer/selection.rb', line 132

def select_word
  coords = word_under_cursor_pos( or_after: true )
  if coords.nil?
    remove_selection
  else
    cursor_to(*coords[0])
    anchor_selection
    cursor_to(*coords[1])
    display
  end
end

#select_word_anotherObject



144
145
146
147
148
149
150
151
152
153
154
# File 'lib/diakonos/buffer/selection.rb', line 144

def select_word_another
  m = selection_mark
  if m.nil?
    select_word
  else
    row, col, _ = pos_of_next( /\w\b/, @last_row, @last_col )
    if row && col
      cursor_to row, col+1, DO_DISPLAY
    end
  end
end

#select_wrapping_blockObject



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 'lib/diakonos/buffer/selection.rb', line 97

def select_wrapping_block
  block_level = indentation_level( @last_row )
  if selecting?
    block_level -= 1
  end
  if block_level <= 0
    return select_all
  end

  end_row = start_row = @last_row

  # Find block end
  ( @last_row...@lines.size ).each do |row|
    next  if @lines[ row ].strip.empty?

    if indentation_level( row ) < block_level
      end_row = row
      break
    end
  end

  # Go to block beginning
  ( 0...@last_row ).reverse_each do |row|
    next  if @lines[ row ].strip.empty?

    if indentation_level( row ) < block_level
      start_row = row + 1
      break
    end
  end

  anchor_selection( end_row, 0 )
  cursor_to( start_row, 0, DO_DISPLAY )
end

#selected_linesObject



259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/diakonos/buffer/selection.rb', line 259

def selected_lines
  selection = selection_mark
  if selection
    if selection.end_col == 0
      end_row = selection.end_row - 1
    else
      end_row = selection.end_row
    end
    @lines[ selection.start_row..end_row ]
  else
    [ @lines[ @last_row ] ]
  end
end

#selected_stringObject



250
251
252
253
254
255
256
257
# File 'lib/diakonos/buffer/selection.rb', line 250

def selected_string
  lines = selected_text
  if lines
    lines.join( "\n" )
  else
    nil
  end
end

#selected_textArray<String>?

Returns:



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/diakonos/buffer/selection.rb', line 233

def selected_text
  selection = selection_mark
  if selection.nil?
    nil
  elsif selection.start_row == selection.end_row
    [ @lines[ selection.start_row ][ selection.start_col...selection.end_col ] ]
  elsif @selection_mode == :block
    @lines[ selection.start_row..selection.end_row ].collect { |line|
      line[ selection.start_col...selection.end_col ]
    }
  else
    [ @lines[ selection.start_row ][ selection.start_col.. ] ] +
    ( @lines[ (selection.start_row + 1)..(selection.end_row - 1) ] || [] ) +
    [ @lines[ selection.end_row ][ 0...selection.end_col ] ]
  end
end

#selecting?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'lib/diakonos/buffer/selection.rb', line 66

def selecting?
  !! selection_mark
end

#selection_markObject



62
63
64
# File 'lib/diakonos/buffer/selection.rb', line 62

def selection_mark
  @text_marks[:selection]
end

#selection_mode_blockObject



273
274
275
# File 'lib/diakonos/buffer/selection.rb', line 273

def selection_mode_block
  @selection_mode = :block
end

#selection_mode_normalObject



277
278
279
# File 'lib/diakonos/buffer/selection.rb', line 277

def selection_mode_normal
  @selection_mode = :normal
end

#set_indent(row, level, opts = {}) ⇒ Object



16
17
18
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
# File 'lib/diakonos/buffer/indentation.rb', line 16

def set_indent( row, level, opts = {} )
  do_display = opts.fetch( :do_display, true )
  undoable   = opts.fetch( :undoable,   true )
  cursor_eol = opts.fetch( :cursor_eol, false )

  @lines[ row ] =~ /^([\s#{@indent_ignore_charset}]*)(.*)$/
  current_indent_text = ( ::Regexp.last_match(1) || "" )
  rest = ( ::Regexp.last_match(2) || "" )
  current_indent_text.gsub!( /\t/, ' ' * @tab_size )
  indentation = @indent_size * [ level, 0 ].max
  if current_indent_text.length >= indentation
    indent_text = current_indent_text[ 0...indentation ]
  else
    indent_text = current_indent_text + " " * ( indentation - current_indent_text.length )
  end
  if @settings[ "lang.#{@language}.indent.using_tabs" ]
    num_tabs = 0
    indent_text.gsub!( / {#{@tab_size}}/ ) do
      num_tabs += 1
      "\t"
    end
    indentation -= num_tabs * ( @tab_size - 1 )
  end

  take_snapshot(typing: true)  if do_display && undoable
  @lines[ row ] = indent_text + rest
  if do_display
    cursor_to(
      row,
      cursor_eol ? @lines[row].length : indentation
    )
  end
  set_modified do_display, modified_from_line: row
end

#set_language(_language) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/diakonos/buffer.rb', line 144

def set_language( _language )
  @settings = $diakonos.settings
  @language = _language
  @surround_pairs = $diakonos.surround_pairs[ @language ]
  @token_regexps = $diakonos.token_regexps[ @language ]
  @close_token_regexps = $diakonos.close_token_regexps[ @language ]
  @token_formats = $diakonos.token_formats[ @language ]
  @indenters = $diakonos.indenters[ @language ]
  @indenters_next_line = $diakonos.indenters_next_line[ @language ]
  @unindenters = $diakonos.unindenters[ @language ]
  @preventers = @settings[ "lang.#{@language}.indent.preventers" ]
  @closers = $diakonos.closers[ @language ] || Hash.new
  @auto_indent = @settings[ "lang.#{@language}.indent.auto" ]
  @indent_size = ( @settings[ "lang.#{@language}.indent.size" ] || 4 )
  @indent_roundup = (
    @settings[ "lang.#{@language}.indent.roundup" ].nil? ?
    true :
    @settings[ "lang.#{@language}.indent.roundup" ]
  )
  @indent_closers = (
    @settings[ "lang.#{@language}.indent.closers" ].nil? ?
    false :
    @settings[ "lang.#{@language}.indent.closers" ]
  )
  @default_formatting = ( @settings[ "lang.#{@language}.format.default" ] || Curses::A_NORMAL )
  @selection_formatting = ( @settings[ "lang.#{@language}.format.selection" ] || Curses::A_REVERSE )
  @indent_ignore_charset = ( @settings[ "lang.#{@language}.indent.ignore.charset" ] || "" )
  @tab_size = ( @settings[ "lang.#{@language}.tabsize" ] || DEFAULT_TAB_SIZE )
end

#set_modified(do_display = DO_DISPLAY, use_md5 = DONT_USE_MD5, modified_from_line: 0) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/diakonos/buffer/file.rb', line 158

def set_modified( do_display = DO_DISPLAY, use_md5 = DONT_USE_MD5, modified_from_line: 0 )
  invalidate_highlight_cache_from( line: modified_from_line )

  if @read_only
    $diakonos.set_iline "Warning: Modifying a read-only file."
  end

  @modified = use_md5 ? file_different? : true
  clear_matches
  @lsp_session&.notify_did_change(buffer: self)

  if do_display
    $diakonos.update_status_line
    display
  end
end

#set_search_area(mark) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/diakonos/buffer/searching.rb', line 33

def set_search_area(mark)
  if mark.nil?
    raise 'Call Diakonos::Buffer#clear_search_area instead'
  end

  @search_area = mark
  @text_marks[ :search_area_pre ] = TextMark.new(
    ::Diakonos::Range.new(0, 0, mark.start_row, mark.start_col),
    @settings[ 'view.non_search_area.format' ]
  )
  @text_marks[ :search_area_post ] = TextMark.new(
    ::Diakonos::Range.new(mark.end_row, mark.end_col, @lines.length - 1, @lines[ -1 ].length),
    @settings[ 'view.non_search_area.format' ]
  )
end

#set_selection(start_row, start_col, end_row, end_col) ⇒ Object



184
185
186
187
188
189
190
# File 'lib/diakonos/buffer/selection.rb', line 184

def set_selection( start_row, start_col, end_row, end_col )
  @text_marks[ :selection ] = TextMark.new(
    ::Diakonos::Range.new(start_row, start_col, end_row, end_col),
    @selection_formatting
  )
  @changing_selection = false
end

#set_selection_current_lineObject



83
84
85
86
87
88
89
# File 'lib/diakonos/buffer/selection.rb', line 83

def set_selection_current_line
  @text_marks[ :selection ] = TextMark.new(
    ::Diakonos::Range.new(@last_row, 0, @last_row, @lines[ @last_row ].size),
    @selection_formatting
  )
  @lines[ @last_row ]
end

#set_type(type) ⇒ Object



652
653
654
655
656
657
658
# File 'lib/diakonos/buffer.rb', line 652

def set_type( type )
  return false  if type.nil?

  configure( type )
  display
  true
end

#show_character(row, col) ⇒ Object

col and row are given relative to the buffer, not any window or screen. Returns true if the view changed positions.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/diakonos/buffer/cursor.rb', line 157

def show_character( row, col )
  old_top_line = @top_line
  old_left_column = @left_column

  while row < @top_line + @settings[ "view.margin.y" ]
    amount = (-1) * @settings[ "view.jump.y" ]
    break  if ( pitch_view( amount, DONT_PITCH_CURSOR, DONT_DISPLAY ) != amount )
  end
  while row > @top_line + $diakonos.main_window_height - 1 - @settings[ "view.margin.y" ]
    amount = @settings[ "view.jump.y" ]
    break  if ( pitch_view( amount, DONT_PITCH_CURSOR, DONT_DISPLAY ) != amount )
  end

  while col < @left_column + @settings[ "view.margin.x" ]
    amount = (-1) * @settings[ "view.jump.x" ]
    break  if ( pan_view( amount, DONT_DISPLAY ) != amount )
  end
  while col > @left_column + $diakonos.main_window_width - @settings[ "view.margin.x" ] - 2
    amount = @settings[ "view.jump.x" ]
    break  if ( pan_view( amount, DONT_DISPLAY ) != amount )
  end

  @top_line != old_top_line || @left_column != old_left_column
end

#surround(text, parenthesis) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/diakonos/buffer.rb', line 226

def surround( text, parenthesis )
  pattern, pair = @surround_pairs.select { |r, _| parenthesis =~ r }.to_a[0]

  if pair.nil?
    $diakonos.set_iline "No matching parentheses pair found."
    nil
  else
    pair.map! do |paren|
      parenthesis.gsub( pattern, paren )
    end
    pair[ 0 ] + text.join( "\n" ) + pair[ 1 ]
  end
end

#tab_expanded_column(col, row) ⇒ Object



5
6
7
8
9
10
11
12
13
14
# File 'lib/diakonos/buffer/indentation.rb', line 5

def tab_expanded_column( col, row )
  delta = 0
  line = @lines[ row ]
  for i in 0...col
    if line[ i ] == "\t"
      delta += ( @tab_size - ( (i+delta) % @tab_size ) ) - 1
    end
  end
  col + delta
end

#take_snapshot(typing: false) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
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
# File 'lib/diakonos/buffer/undo.rb', line 8

def take_snapshot( typing: false )
  do_it = false

  if (
    ! @modified &&
    file_modified? &&
    file_different? &&
    $diakonos.revert( "File has been altered externally.  Load on-disk version?" )
  )
    return
  end

  if @typing != typing
    @typing = typing
    # If we just started typing, take a snapshot, but don't continue
    # taking snapshots for every keystroke
    if typing
      do_it = true
    end
  end
  if ! @typing
    do_it = true
  end

  if do_it
    undo_size = 0
    @buffer_states[ 1.. ].each do |state|
      undo_size += state.length
    end
    while ( ( undo_size + @lines.length ) >= @settings[ "max_undo_lines" ] ) && @buffer_states.length > 1
      @cursor_states.pop
      popped_state = @buffer_states.pop
      undo_size = undo_size - popped_state.length
    end

    if @current_buffer_state > 0
      @buffer_states.unshift @lines.deep_clone
      @cursor_states.unshift [ @last_row, @last_col ]
    end

    @buffer_states.unshift @lines.deep_clone
    @cursor_states.unshift [ @last_row, @last_col ]
    @current_buffer_state = 0
    @lines = @buffer_states[ @current_buffer_state ]
  end
end

#to_aObject



188
189
190
# File 'lib/diakonos/buffer.rb', line 188

def to_a
  @lines.dup
end

#toggle_bookmarkObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/diakonos/buffer/bookmarking.rb', line 30

def toggle_bookmark
  bookmark = Bookmark.new( self, @last_row, @last_col )
  existing = @bookmarks.find { |bm|
    bm == bookmark
  }
  if existing
    @bookmarks.delete existing
    $diakonos.set_iline "Bookmark #{existing} deleted."
  else
    @bookmarks.push bookmark
    @bookmarks.sort
    $diakonos.set_iline "Bookmark #{bookmark} set."
  end
end

#toggle_selectionObject



220
221
222
223
224
225
226
# File 'lib/diakonos/buffer/selection.rb', line 220

def toggle_selection
  if @changing_selection
    remove_selection
  else
    anchor_selection
  end
end

#truncate_off_screen(string, write_cursor_col) ⇒ Object

Prints text to the screen, truncating where necessary. Returns nil if the string is completely off-screen. write_cursor_col is buffer-relative, not screen-relative



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/diakonos/buffer/display.rb', line 77

def truncate_off_screen( string, write_cursor_col )
  retval = string

  # Truncate based on left edge of display area
  if write_cursor_col < @left_column
    retval = retval[ (@left_column - write_cursor_col).. ]
    write_cursor_col = @left_column
  end

  if retval && (
    # Truncate based on right edge of display area
    write_cursor_col + retval.length > @left_column + Curses.cols - 1
  )
    new_length = ( @left_column + Curses.cols - write_cursor_col )
    if new_length <= 0
      retval = nil
    else
      retval = retval[ 0...new_length ]
    end
  end

  retval == "" ? nil : retval
end

#uncommentObject



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

def uncomment
  take_snapshot
  comment_string = Regexp.escape( @settings[ "lang.#{@language}.comment_string" ].to_s )
  comment_close_string = Regexp.escape( @settings[ "lang.#{@language}.comment_close_string" ].to_s )
  one_modified = false
  self.selected_lines.each do |line|
    old_line = line.dup
    comment_regexp = /^(\s*)#{comment_string}/
    line.gsub!( comment_regexp, "\\1" )
    if line !~ comment_regexp
      line.gsub!( /#{comment_close_string}$/, '' )
    end
    one_modified ||= ( line != old_line )
  end
  if one_modified
    set_modified modified_from_line: (@text_marks[:selection]&.start_row || @last_row)
  end
end

#undoObject



55
56
57
58
59
60
61
62
63
# File 'lib/diakonos/buffer/undo.rb', line 55

def undo
  return  if @current_buffer_state >= @buffer_states.length - 1

  @current_buffer_state += 1
  @lines = @buffer_states[ @current_buffer_state ]
  cursor_to( @cursor_states[ @current_buffer_state - 1 ][ 0 ], @cursor_states[ @current_buffer_state - 1 ][ 1 ] )
  $diakonos.set_iline "Undo level: #{@current_buffer_state} of #{@buffer_states.length - 1}"
  set_modified DO_DISPLAY, DO_USE_MD5, modified_from_line: 0
end

#unindent(row = @last_row, do_display = DO_DISPLAY) ⇒ Object



171
172
173
174
# File 'lib/diakonos/buffer/indentation.rb', line 171

def unindent( row = @last_row, do_display = DO_DISPLAY )
  level = indentation_level( row, DONT_USE_INDENT_IGNORE )
  set_indent  row, level - 1, do_display:
end

#unundoObject

Since redo is a Ruby keyword…



66
67
68
69
70
71
72
73
74
# File 'lib/diakonos/buffer/undo.rb', line 66

def unundo
  return  if @current_buffer_state <= 0

  @current_buffer_state += -1
  @lines = @buffer_states[ @current_buffer_state ]
  cursor_to( @cursor_states[ @current_buffer_state ][ 0 ], @cursor_states[ @current_buffer_state ][ 1 ] )
  $diakonos.set_iline "Undo level: #{@current_buffer_state} of #{@buffer_states.length - 1}"
  set_modified DO_DISPLAY, DO_USE_MD5, modified_from_line: 0
end

#word_before_cursorObject



687
688
689
690
691
692
693
694
695
696
697
698
699
# File 'lib/diakonos/buffer.rb', line 687

def word_before_cursor
  word = nil

  @lines[ @last_row ].scan( WORD_REGEXP ) do |match_text|
    last_match = Regexp.last_match
    if last_match.begin( 0 ) <= @last_col && @last_col <= last_match.end( 0 )
      word = match_text
      break
    end
  end

  word
end

#word_under_cursorObject



660
661
662
663
664
665
666
667
# File 'lib/diakonos/buffer.rb', line 660

def word_under_cursor
  pos = word_under_cursor_pos
  return  if pos.nil?

  col1 = pos[ 0 ][ 1 ]
  col2 = pos[ 1 ][ 1 ]
  @lines[ @last_row ][ col1...col2 ]
end

#word_under_cursor_pos(options = {}) ⇒ Object



669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
# File 'lib/diakonos/buffer.rb', line 669

def word_under_cursor_pos( options = {} )
  or_after = options[:or_after]
  @lines[ @last_row ].scan( WORD_REGEXP ) do
    last_match = Regexp.last_match
    if (
      last_match.begin( 0 ) <= @last_col && @last_col < last_match.end( 0 ) ||
      or_after && last_match.begin(0) > @last_col
    )
      return [
        [ @last_row, last_match.begin( 0 ) ],
        [ @last_row, last_match.end( 0 ) ],
      ]
    end
  end

  nil
end

#words(filter_regexp = nil) ⇒ Object

TODO paragraph_before_cursor(_pos)?



744
745
746
747
# File 'lib/diakonos/buffer.rb', line 744

def words( filter_regexp = nil )
  w = @lines.join( ' ' ).scan( WORD_REGEXP )
  filter_regexp ? w.grep( filter_regexp ) : w
end

#wrap_paragraphObject



594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
# File 'lib/diakonos/buffer.rb', line 594

def wrap_paragraph
  start_row = end_row = @last_row
  until start_row == 0 || @lines[ start_row - 1 ].strip == ''
    start_row -= 1
  end
  until end_row == @lines.size || @lines[ end_row ].strip == ''
    end_row += 1
  end

  lines = []
  line = ''
  words = @lines[ start_row...end_row ].join( ' ' ).scan( /\S+/ )
  words.each do |word|
    if word =~ /^[a-z']+[.!?]$/
      word = "#{word} "
    end
    if line.length + word.length + 1 > ( @settings[ "lang.#{@language}.wrap_margin" ] || 80 )
      lines << line.strip
      line = ''
    end
    line << " #{word}"
  end
  line.strip!
  if ! line.empty?
    lines << line
  end
  if @lines[ start_row...end_row ] != lines
    take_snapshot
    @lines[ start_row...end_row ] = lines
    set_modified modified_from_line: start_row
    cursor_to start_row + lines.length, lines[-1].length
  end
end