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

TYPING =
true
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
MATCH_CLOSE =
true
MATCH_ANY =
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+/
DO_USE_MD5 =
true
DONT_USE_MD5 =
false
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:


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/diakonos/buffer.rb', line 46

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

  @buffer_states = Array.new
  @cursor_states = Array.new
  if @name.nil?
    @lines = Array.new
    @lines[ 0 ] = ""
  else
    @name = File.expand_path( @name )
    if FileTest.exists? @name
      @lines = IO.readlines( @name )
      if ( @lines.length == 0 ) || ( @lines[ -1 ][ -1..-1 ] == "\n" )
        @lines.push ""
      end
      @lines = @lines.collect do |line|
        line.chomp
      end
    else
      @lines = Array.new
      @lines[ 0 ] = ""
    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
  @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
  @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


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

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


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

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


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

def left_column
  @left_column
end

#linesObject

Returns the value of attribute lines


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

def lines
  @lines
end

#nameObject (readonly)

Returns the value of attribute name


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

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


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

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


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

def selection_mode
  @selection_mode
end

#tab_sizeObject (readonly)

Returns the value of attribute tab_size


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

def tab_size
  @tab_size
end

#top_lineObject (readonly)

Returns the value of attribute top_line


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

def top_line
  @top_line
end

Instance Method Details

#==(other) ⇒ Object


159
160
161
162
# File 'lib/diakonos/buffer.rb', line 159

def == (other)
  return false  if other.nil?
  @name == other.name
end

#[](arg) ⇒ Object


155
156
157
# File 'lib/diakonos/buffer.rb', line 155

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

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


67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/diakonos/buffer/searching.rb', line 67

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
  if index = @lines[ from_row ].index( regexp, index_col )
    if index <= ( @last_finding ? @last_finding.start_col : from_col )
      finding = confirm_finding( regexps, search_area, from_row, index, Regexp.last_match )
      return finding  if finding
    end
  end
end

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


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 131

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|
    if index = @lines[ i ].rindex( regexp )
      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|
    if index = @lines[ i ].rindex( regexp )
      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
  if index = @lines[ from_row ].rindex( regexp )
    if index > search_col
      finding = confirm_finding( regexps, search_area, from_row, index, Regexp.last_match )
      return finding  if finding
    end
  end
end

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


179
180
181
182
183
184
185
186
187
# File 'lib/diakonos/buffer/selection.rb', line 179

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


189
190
191
192
193
194
195
# File 'lib/diakonos/buffer/selection.rb', line 189

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

#carriage_returnObject


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

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

#clear_matches(do_display = DONT_DISPLAY) ⇒ Object


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

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

#clear_pair_highlightObject


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

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


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

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, !TYPING, @indent_closers
      cursor_to r, c
      if /%_/ === str
        find [/%_/], direction: :down, replacement: '', auto_choice: CHOICE_YES_AND_STOP
      end
    end
  end
end

#collapse_whitespaceObject


288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/diakonos/buffer.rb', line 288

def collapse_whitespace
  if selection_mark
    remove_selection DONT_DISPLAY
  end

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

#column_of(x) ⇒ Object

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


419
420
421
# File 'lib/diakonos/buffer.rb', line 419

def column_of( x )
  @left_column + x
end

#column_to_x(col) ⇒ Object

Returns nil if the column is off-screen.


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

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


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

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 = $1
      del = $2
      post = $3
      if pre !~ /\s$/
        del = " #{del}"
      end
      if post !~ /^\s/
        del = "#{del} "
      end
      del.sub!( /^\s+/, ' ' * num_spaces_padding )
      del.sub!( /\s+$/, ' ' * num_spaces_padding )
      line.replace( ( "%-#{column_width}s" % pre ) + del + post )
    end
    one_modified ||= ( line != old_line )
  end

  if one_modified
    set_modified
  end
end

#comment_outObject


345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/diakonos/buffer.rb', line 345

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
  end
end

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


116
117
118
119
120
121
122
123
124
125
126
# File 'lib/diakonos/buffer.rb', line 116

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
end

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


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

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


576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/diakonos/buffer.rb', line 576

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


214
215
216
# File 'lib/diakonos/buffer/selection.rb', line 214

def copy_selection
  selected_text
end

#current_columnObject


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

def current_column
  @last_col
end

#current_lineObject


409
410
411
# File 'lib/diakonos/buffer.rb', line 409

def current_line
  @lines[ @last_row ]
end

#current_rowObject


444
445
446
# File 'lib/diakonos/buffer.rb', line 444

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


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

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 )
        @lines[ row ] = line[ 0...col ] + line[ (col + 1)..-1 ]
        set_modified
      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..-1 ]
    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
  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

  retval
end

#delete_selection(do_display = DO_DISPLAY) ⇒ Object


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/selection.rb', line 268

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..-1 ]
  else
    case @selection_mode
    when :normal
      end_line = @lines[ end_row ]
      @lines[ start_row ] = start_line[ 0...start_col ] + end_line[ end_col..-1 ]
      @lines = @lines[ 0..start_row ] + @lines[ (end_row + 1)..-1 ]
    when :block
      @lines[ start_row..end_row ] = @lines[ start_row..end_row ].collect { |line|
        line[ 0...start_col ] + ( line[ end_col..-1 ] || '' )
      }
    end
  end

  cursor_to start_row, start_col
  remove_selection DONT_DISPLAY
  set_modified do_display
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..-1 ] ]
    @lines[ row ] = @lines[ row ][ 0...col ]
  end
  set_modified

  retval
end

#displayObject


272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
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
# File 'lib/diakonos/buffer/display.rb', line 272

def display
  @continued_format_class = nil
  @pen_down = true
  @lang_stack = []

  # First, we have to "draw" off-screen, in order to check for opening of
  # multi-line highlights.

  # So, first look backwards from the @top_line to find the first opening
  # regexp match, if any.
  index = @top_line - 1
  @lines[ [ 0, @top_line - @settings[ "view.lookback" ] ].max...@top_line ].reverse_each do |line|
    open_index = -1
    open_token_class = nil
    open_match_text = nil

    open_index, open_token_class, open_match_text = find_opening_match( line )

    if open_token_class
      @pen_down = false
      @lines[ index...@top_line ].each do |line|
        print_line line
      end
      @pen_down = true

      break
    end

    index = index - 1
  end

  # 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..-1 ]
        ]
      )
    end
    @win_main.setpos( y, 0 )
    print_line line.expand_tabs( @tab_size )
    @win_main.setpos( y, 0 )
    paint_marks @top_line + row
    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' ] )
    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

  if @win_line_numbers
    @win_line_numbers.refresh
  end
  @win_main.setpos( @last_screen_y , @last_screen_x )
  @win_main.refresh

  if @language != @original_language
    set_language( @original_language )
  end
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)

130
131
132
133
134
135
136
137
138
139
140
# File 'lib/diakonos/buffer/file.rb', line 130

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


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/diakonos/buffer/file.rb', line 109

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
# 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+)/ ) { |m|
        ref = $1
        if ref == "\\"
          "\\"
        else
          finding.captured_group(ref.to_i)
        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: direction, replacement: replacement )
      when CHOICE_ALL
        num_replacements += replace_all(regexp, replacement)
      when CHOICE_WITHIN_SELECTION
        num_replacements += replace_all(regexp, replacement, :within_search_area)
      when CHOICE_NO
        num_replacements += find( regexps, direction: direction, replacement: 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


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

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: direction
  end
end

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


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

def find_opening_match( line, match_close = true, bos_allowed = 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|
    if match = regexp.match( line )
      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
      if ( ! regexp.uses_bos ) || ( bos_allowed && ( whole_match_index == 0 ) )
        if index < open_index
          if ( ( ! match_close ) || @close_token_regexps[ token_class ] )
            open_index = index
            open_token_class = token_class
            open_match_text = match_text
          end
        end
      end
    end
  end

  [ open_index, open_token_class, open_match_text ]
end

#go_block_innerObject


208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# 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


225
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 225

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
    else
      if level == initial_level
        new_row = row
        break
      elsif level < initial_level - 1
        break
      end
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end

#go_block_outerObject


185
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 185

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

#go_to_char(char, after = ON_CHAR) ⇒ Object


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

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


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

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


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

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 do |bm|
    bm > cur_pos
  end
  if next_bm
    cursor_to( next_bm.row, next_bm.col, DO_DISPLAY )
  end
end

#go_to_pair_matchObject


429
430
431
432
433
434
435
436
437
# File 'lib/diakonos/buffer/searching.rb', line 429

def go_to_pair_match
  row, col = pos_of_pair_match
  if row && col
    if cursor_to( row, col )
      highlight_pair
      display
    end
  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


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

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


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

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 )
  n = grepped_lines.count
  found_marks = grepped_lines.collect do |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"]
    )
  end
  @text_marks[:found] = found_marks
  @num_matches_found ||= found_marks.size
end

#highlight_pairObject


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

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.


414
415
416
# File 'lib/diakonos/buffer.rb', line 414

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

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


163
164
165
166
# File 'lib/diakonos/buffer/indentation.rb', line 163

def indent( row = @last_row, do_display = DO_DISPLAY )
  level = indentation_level( row, DONT_USE_INDENT_IGNORE )
  set_indent  row, level + 1, do_display: 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 = $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 = $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


188
189
190
191
192
193
194
195
# File 'lib/diakonos/buffer.rb', line 188

def insert_char( c )
  row = @last_row
  col = @last_col
  take_snapshot( TYPING )
  line = @lines[ row ]
  @lines[ row ] = line[ 0...col ] + c.chr + line[ col..-1 ]
  set_modified
end

#insert_string(str) ⇒ Object


197
198
199
200
201
202
203
204
# File 'lib/diakonos/buffer.rb', line 197

def insert_string( str )
  row = @last_row
  col = @last_col
  take_snapshot( TYPING )
  line = @lines[ row ]
  @lines[ row ] = line[ 0...col ] + str + line[ col..-1 ]
  set_modified
end

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


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

def join_lines( row = @last_row, strip = DONT_STRIP_LINE )
  take_snapshot( TYPING )
  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
end

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


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

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
end

#lengthObject


164
165
166
# File 'lib/diakonos/buffer.rb', line 164

def length
  @lines.length
end

#line_at(y) ⇒ Object


400
401
402
403
404
405
406
407
# File 'lib/diakonos/buffer.rb', line 400

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

#modified?Boolean

Returns:

  • (Boolean)

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

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, false)
      if row_before && @lines[row_before] =~ @indenters_next_line
        row = row_before
        next
      end
    end

    break
  end

  row
end

#nice_nameObject


176
177
178
# File 'lib/diakonos/buffer.rb', line 176

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

#paint_column_markersObject


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

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


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
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/diakonos/buffer/display.rb', line 101

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..-1 ]
        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.


89
90
91
92
93
94
95
96
97
98
99
# File 'lib/diakonos/buffer/display.rb', line 89

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.


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

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


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

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.


650
651
652
653
# File 'lib/diakonos/buffer.rb', line 650

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.


657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
# File 'lib/diakonos/buffer.rb', line 657

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
# 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

      indenter_index = (prev_line =~ @indenters)
      nl_indenter_index = (prev_line =~ @indenters_next_line)

      if nl_indenter_index && basis_row == row-1
        level += 1
      elsif indenter_index
        level += 1
        unindenter_index = (prev_line =~ @unindenters)
        if unindenter_index && unindenter_index != indenter_index
          level -= 1
        end
      end

      if line =~ @unindenters
        level -= 1
      end
    end
  end

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

#paste(text, typing = ! TYPING,, do_parsed_indent = false) ⇒ Object

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


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

def paste( text, typing = ! TYPING, do_parsed_indent = 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..-1 ]
    if do_parsed_indent
      parsed_indent  row: 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..-1 ]
      @lines[ row + 1, 0 ] = text[ 1..-2 ]
      new_col = column_of( text[ -1 ].length )
    when :block
      @lines += [ '' ] * [ 0, ( row + text.length - @lines.length ) ].max
      @lines[ row...( row + text.length ) ] = @lines[ row...( row + text.length ) ].collect.with_index { |line,index|
        pre = line[ 0...col ].ljust( col )
        post = line[ col..-1 ]
        "#{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
end

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

Returns the amount the view was actually pitched.


537
538
539
# File 'lib/diakonos/buffer.rb', line 537

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


465
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
531
532
533
534
# File 'lib/diakonos/buffer.rb', line 465

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


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

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


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

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


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

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.


172
173
174
175
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
# File 'lib/diakonos/buffer/display.rb', line 172

def print_line( line )
  i = 0
  substr = nil
  index = nil
  while i < line.length
    substr = line[ i..-1 ]
    if @continued_format_class
      close_index, close_match_text = find_closing_match(
        substr,
        @close_token_regexps[@continued_format_class],
        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, MATCH_ANY, i == 0 )

      if @lang_stack.length > 0
        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],
          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 ]
          if change_to = @settings[ "lang.#{@language}.tokens.#{first_token_class}.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

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

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

160
161
162
163
164
165
166
# File 'lib/diakonos/buffer/display.rb', line 160

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
# 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
    else
      if ccol < acol
        anchor_first = false
      end
    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
    else
      if 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
end

#remove_selection(do_display = DO_DISPLAY) ⇒ Object


197
198
199
200
201
202
203
204
# File 'lib/diakonos/buffer/selection.rb', line 197

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


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

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
  clear_matches
  display
  num_replacements
end

#replace_char(c) ⇒ Object


180
181
182
183
184
185
186
# File 'lib/diakonos/buffer.rb', line 180

def replace_char( c )
  row = @last_row
  col = @last_col
  take_snapshot TYPING
  @lines[ row ][ col ] = c
  set_modified
end

#reset_displayObject


128
129
130
131
# File 'lib/diakonos/buffer.rb', line 128

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.


424
425
426
# File 'lib/diakonos/buffer.rb', line 424

def row_of( y )
  @top_line + y
end

#row_to_y(row) ⇒ Object

Returns nil if the row is off-screen.


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

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
# 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.exists?( @name ) && FileTest.exists?( 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.exists?( 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 =~ /#{$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.


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

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|
    @lines[ 0..-2 ].each do |line|
      if @settings[ 'strip_trailing_whitespace_on_save' ]
        line.rstrip!
      end
      f.puts line
    end

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

    if @settings[ 'strip_trailing_whitespace_on_save' ]
      if @last_col > @lines[ @last_row ].size
        cursor_to @last_row, @lines[ @last_row ].size
      end
    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:


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

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..-1]
  lines[-1] = lines[-1][0..@search_area.end_col]

  lines
end

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


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

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


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/selection.rb', line 146

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..-1 ].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


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

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


62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/diakonos/buffer/selection.rb', line 62

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


122
123
124
125
126
127
128
129
130
131
132
# File 'lib/diakonos/buffer/selection.rb', line 122

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


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

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


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

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


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

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


238
239
240
241
242
243
244
245
# File 'lib/diakonos/buffer/selection.rb', line 238

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

#selected_textArray<String>?

Returns:


219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/diakonos/buffer/selection.rb', line 219

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 ] ]
  else
    if @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..-1 ] ] +
        ( @lines[ (selection.start_row + 1) .. (selection.end_row - 1) ] || [] ) +
        [ @lines[ selection.end_row ][ 0...selection.end_col ] ]
    end
  end
end

#selecting?Boolean

Returns:

  • (Boolean)

58
59
60
# File 'lib/diakonos/buffer/selection.rb', line 58

def selecting?
  !! selection_mark
end

#selection_markObject


54
55
56
# File 'lib/diakonos/buffer/selection.rb', line 54

def selection_mark
  @text_marks[:selection]
end

#selection_mode_blockObject


261
262
263
# File 'lib/diakonos/buffer/selection.rb', line 261

def selection_mode_block
  @selection_mode = :block
end

#selection_mode_normalObject


264
265
266
# File 'lib/diakonos/buffer/selection.rb', line 264

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 = ( $1 || "" )
  rest = ( $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}}/ ) { |match|
      num_tabs += 1
      "\t"
    }
    indentation -= num_tabs * ( @tab_size - 1 )
  end

  take_snapshot( TYPING )  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
end

#set_language(language) ⇒ Object


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/diakonos/buffer.rb', line 133

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) ⇒ Object


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

def set_modified( do_display = DO_DISPLAY, use_md5 = DONT_USE_MD5 )
  if @read_only
    $diakonos.set_iline "Warning: Modifying a read-only file."
  end

  @modified = use_md5 ? file_different? : true
  clear_matches
  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
48
# File 'lib/diakonos/buffer/searching.rb', line 33

def set_search_area(mark)
  if mark.nil?
    raise 'Call Diakonos::Buffer#clear_search_area instead'
    return
  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


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

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


75
76
77
78
79
80
81
# File 'lib/diakonos/buffer/selection.rb', line 75

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


600
601
602
603
604
605
# File 'lib/diakonos/buffer.rb', line 600

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.


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

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


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

def surround( text, parenthesis )
  pattern, pair = @surround_pairs.select { |r, p| 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
# File 'lib/diakonos/buffer/undo.rb', line 8

def take_snapshot( typing = false )
  do_it = false

  if ! @modified && file_modified? && file_different?
    return  if $diakonos.revert( "File has been altered externally.  Load on-disk version?" )
  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..-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


168
169
170
# File 'lib/diakonos/buffer.rb', line 168

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 do |bm|
    bm == bookmark
  end
  if existing
    @bookmarks.delete existing
    $diakonos.set_iline "Bookmark #{existing.to_s} deleted."
  else
    @bookmarks.push bookmark
    @bookmarks.sort
    $diakonos.set_iline "Bookmark #{bookmark.to_s} set."
  end
end

#toggle_selectionObject


206
207
208
209
210
211
212
# File 'lib/diakonos/buffer/selection.rb', line 206

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


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/diakonos/buffer/display.rb', line 64

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)..-1 ]
    write_cursor_col = @left_column
  end

  if retval
    # Truncate based on right edge of display area
    if 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
  end

  retval == "" ? nil : retval
end

#uncommentObject


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

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
  end
end

#undoObject


50
51
52
53
54
55
56
57
58
# File 'lib/diakonos/buffer/undo.rb', line 50

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
end

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


168
169
170
171
# File 'lib/diakonos/buffer/indentation.rb', line 168

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

#unundoObject

Since redo is a Ruby keyword…


61
62
63
64
65
66
67
68
69
# File 'lib/diakonos/buffer/undo.rb', line 61

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
end

#word_before_cursorObject


634
635
636
637
638
639
640
641
642
643
644
645
646
# File 'lib/diakonos/buffer.rb', line 634

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


607
608
609
610
611
612
613
614
# File 'lib/diakonos/buffer.rb', line 607

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


616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
# File 'lib/diakonos/buffer.rb', line 616

def word_under_cursor_pos( options = {} )
  or_after = options[:or_after]
  @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 ) ||
      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)?


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

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

#wrap_paragraphObject


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

def wrap_paragraph
  start_row = end_row = cursor_row = @last_row
  cursor_col = @last_col
  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
    cursor_to start_row + lines.length, lines[-1].length
  end
end