Class: Diakonos::Buffer

Inherits:
Object show all
Defined in:
lib/diakonos/buffer.rb,
lib/diakonos/buffer/undo.rb,
lib/diakonos/buffer/file.rb,
lib/diakonos/buffer/cursor.rb,
lib/diakonos/buffer/delete.rb,
lib/diakonos/buffer/display.rb,
lib/diakonos/buffer/selection.rb,
lib/diakonos/buffer/searching.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 },
}

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.

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:


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

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


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

#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


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

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

#[](arg) ⇒ Object


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

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

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


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

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


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

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

#carriage_returnObject


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

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


357
358
359
360
361
# File 'lib/diakonos/buffer/searching.rb', line 357

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

#clear_pair_highlightObject


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

def clear_pair_highlight
  @text_marks[ :pair ] = nil
end

#close_codeObject


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

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


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

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.


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

def column_of( x )
  @left_column + x
end

#column_to_x(col) ⇒ Object

Returns nil if the column is off-screen.


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

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


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

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


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

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


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

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

#contextObject


555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
# File 'lib/diakonos/buffer.rb', line 555

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


217
218
219
# File 'lib/diakonos/buffer/selection.rb', line 217

def copy_selection
  selected_text
end

#current_columnObject


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

def current_column
  @last_col
end

#current_lineObject


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

def current_line
  @lines[ @last_row ]
end

#current_rowObject


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

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
# 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 = row.fit( 0, @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


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/cursor.rb', line 91

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


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

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

#cursor_to_eofObject


87
88
89
# File 'lib/diakonos/buffer/cursor.rb', line 87

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

#cursor_to_eolObject


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

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


142
143
144
# File 'lib/diakonos/buffer/cursor.rb', line 142

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


271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/diakonos/buffer/selection.rb', line 271

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


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

def display
  @continued_format_class = nil
  @pen_down = true

  # 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

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


45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/diakonos/buffer/searching.rb', line 45

def establish_finding( regexps, search_area, from_row, from_col, match )
  found_text = match[ 0 ]
  finding = Finding.new( from_row, from_col, from_row, from_col + found_text.length )
  if finding.match( regexps, @lines )
    if(
      search_area.contains?( finding.start_row, finding.start_col ) &&
      search_area.contains?( finding.end_row, finding.end_col - 1 )
    )
      throw :found, [ finding, match ]
    end
  end
end

#file_different?Boolean

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


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.


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 = {}) ⇒ Fixnum

Takes an array of Regexps, which represents a user-provided regexp, split across newline characters. Once the first element is found, each successive element must match against lines following the first element.


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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
170
171
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
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
# File 'lib/diakonos/buffer/searching.rb', line 74

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

  search_area = @search_area || TextMark.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
    case @last_search_direction
    when :up
      direction = :down
    else
      direction = :up
    end
  end
  @last_search_regexps = regexps
  @last_search_direction = direction

  wrapped = false

  finding, match = catch :found do

    if direction == :down

      # Check the current row first.

      index = @lines[ from_row ].index(
        regexp,
        ( @last_finding ? @last_finding.start_col : from_col ) + 1
      )
      if index
        establish_finding( regexps, search_area, from_row, index, Regexp.last_match )
      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
          establish_finding( regexps, search_area, i, index, Regexp.last_match )
        end
      end

      if index
        establish_finding( regexps, search_area, search_area.end_row, index, Regexp.last_match )
      end

      # Wrap around.

      wrapped = true

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

      ( search_area.start_row+1...from_row ).each do |i|
        index = @lines[ i ].index( regexp )
        if index
          establish_finding( regexps, search_area, i, index, Regexp.last_match )
        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 )
          establish_finding( regexps, search_area, from_row, index, Regexp.last_match )
        end
      end

    elsif direction == :up

      # 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 ) )
        establish_finding( regexps, search_area, from_row, index, Regexp.last_match )
      end

      # Check above the cursor.

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

      # Wrap around.

      wrapped = true

      (@lines.length - 1).downto(from_row + 1) do |i|
        if index = @lines[ i ].rindex( regexp )
          establish_finding( regexps, search_area, i, index, Regexp.last_match )
        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
          establish_finding( regexps, search_area, from_row, index, Regexp.last_match )
        end
      end
    end
  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
          match[ 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


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

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_closing_match(line_, regexp, bos_allowed = true, start_at = 0) ⇒ Object


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

def find_closing_match( line_, regexp, bos_allowed = true, start_at = 0 )
  close_match_text = nil
  close_index = nil
  if start_at > 0
    line = line_[ start_at..-1 ]
  else
    line = line_
  end
  line.scan( regexp ) do |m|
    match = Regexp.last_match
    if match.length > 1
      index = match.begin 1
      match_text = match[ 1 ]
    else
      index = match.begin 0
      match_text = match[ 0 ]
    end
    if ( ! regexp.uses_bos ) || ( bos_allowed && ( index == 0 ) )
      close_index = index
      close_match_text = match_text
      break
    end
  end

  [ close_index, close_match_text ]
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


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

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


221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/diakonos/buffer/cursor.rb', line 221

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


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/diakonos/buffer/cursor.rb', line 181

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


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

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


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

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


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

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


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

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


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

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


527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/diakonos/buffer/searching.rb', line 527

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


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

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


431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/diakonos/buffer/searching.rb', line 431

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(
      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.


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

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


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

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


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

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


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

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


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

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


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

def length
  @lines.length
end

#line_at(y) ⇒ Object


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

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

#modified?Boolean


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

def modified?
  @modified
end

#nearest_basis_row_from(starting_row, next_line_check = true) ⇒ Fixnum


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


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

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

#paint_column_markersObject


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

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


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

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.


93
94
95
96
97
98
99
100
101
102
103
# File 'lib/diakonos/buffer/display.rb', line 93

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.


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

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


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

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.


629
630
631
632
# File 'lib/diakonos/buffer.rb', line 629

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.


636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
# File 'lib/diakonos/buffer.rb', line 636

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”)


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/diakonos/buffer/selection.rb', line 304

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.


516
517
518
# File 'lib/diakonos/buffer.rb', line 516

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


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

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 = @last_row.fit( @top_line, @top_line + height - 1 )
  if @last_row - @top_line < @settings[ "view.margin.y" ]
    @last_row = @top_line + @settings[ "view.margin.y" ]
    @last_row = @last_row.fit( @top_line, @top_line + height - 1 )
  elsif @top_line + height - 1 - @last_row < @settings[ "view.margin.y" ]
    @last_row = @top_line + height - 1 - @settings[ "view.margin.y" ]
    @last_row = @last_row.fit( @top_line, @top_line + height - 1 )
  end
  @last_col = @last_col.fit( @left_column, [ @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


363
364
365
366
367
368
369
370
371
372
373
# File 'lib/diakonos/buffer/searching.rb', line 363

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


393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/diakonos/buffer/searching.rb', line 393

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


375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/diakonos/buffer/searching.rb', line 375

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 setup 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.


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

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
          if close_index > 0
            # Print any remaining text in the embedded language
            print_string truncate_off_screen( substr[ 0...close_index ], i )
            i += substr[ 0...close_index ].length
          end

          @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

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

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

164
165
166
167
168
169
170
# File 'lib/diakonos/buffer/display.rb', line 164

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( arow, acol, crow, ccol, @selection_formatting )
    else
      @text_marks[ :selection ] = TextMark.new( crow, ccol, arow, acol, @selection_formatting )
    end
  when :block
    if crow < arow
      if ccol < acol # Northwest
        @text_marks[ :selection ] = TextMark.new( crow, ccol, arow, acol, @selection_formatting )
      else           # Northeast
        @text_marks[ :selection ] = TextMark.new( crow, acol, arow, ccol, @selection_formatting )
      end
    else
      if ccol < acol  # Southwest
        @text_marks[ :selection ] = TextMark.new( arow, ccol, crow, acol, @selection_formatting )
      else            # Southeast
        @text_marks[ :selection ] = TextMark.new( arow, acol, crow, ccol, @selection_formatting )
      end
    end
  end
end

#remove_selection(do_display = DO_DISPLAY) ⇒ Object


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

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


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

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


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

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

#reset_displayObject


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

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.


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

def row_of( y )
  @top_line + y
end

#row_to_y(row) ⇒ Object

Returns nil if the row is off-screen.


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

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=(mark) ⇒ Object


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/diakonos/buffer/searching.rb', line 22

def search_area=( mark )
  @search_area = mark
  if mark.nil?
    @text_marks.delete :search_area_pre
    @text_marks.delete :search_area_post
  else
    @text_marks[ :search_area_pre ] = TextMark.new(
      0,
      0,
      mark.start_row,
      mark.start_col,
      @settings[ 'view.non_search_area.format' ]
    )
    @text_marks[ :search_area_post ] = TextMark.new(
      mark.end_row,
      mark.end_col,
      @lines.length - 1,
      @lines[ -1 ].length,
      @settings[ 'view.non_search_area.format' ]
    )
  end
end

#search_area?Boolean


18
19
20
# File 'lib/diakonos/buffer/searching.rb', line 18

def search_area?
  @search_area
end

#search_area_linesArray<String>


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

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


459
460
461
462
463
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
# File 'lib/diakonos/buffer/searching.rb', line 459

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


149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/diakonos/buffer/selection.rb', line 149

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


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

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


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

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


137
138
139
140
141
142
143
144
145
146
147
# File 'lib/diakonos/buffer/selection.rb', line 137

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


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

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


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

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


241
242
243
244
245
246
247
248
# File 'lib/diakonos/buffer/selection.rb', line 241

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

#selected_textArray<String>?


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

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


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


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

def selection_mode_block
  @selection_mode = :block
end

#selection_mode_normalObject


267
268
269
# File 'lib/diakonos/buffer/selection.rb', line 267

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


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

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_selection(start_row, start_col, end_row, end_col) ⇒ Object


177
178
179
180
# File 'lib/diakonos/buffer/selection.rb', line 177

def set_selection( start_row, start_col, end_row, end_col )
  @text_marks[ :selection ] = TextMark.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
82
83
84
# File 'lib/diakonos/buffer/selection.rb', line 75

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

#set_type(type) ⇒ Object


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

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.


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

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


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

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


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

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


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

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


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

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


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

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


613
614
615
616
617
618
619
620
621
622
623
624
625
# File 'lib/diakonos/buffer.rb', line 613

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


586
587
588
589
590
591
592
593
# File 'lib/diakonos/buffer.rb', line 586

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


595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
# File 'lib/diakonos/buffer.rb', line 595

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)?


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

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

#wrap_paragraphObject


520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
# File 'lib/diakonos/buffer.rb', line 520

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