class Diakonos::Buffer

Constants

CHARACTER_PAIRS
DONT_DISPLAY
DONT_PAD_END
DONT_PITCH_CURSOR
DONT_STRIP_LINE
DONT_USE_INDENT_IGNORE
DONT_USE_MD5
DO_DISPLAY
DO_PITCH_CURSOR
DO_USE_MD5
MATCH_ANY
MATCH_CLOSE
NO_SNAPSHOT
OPPOSITE_DIRECTION
PAD_END
READ_ONLY
READ_WRITE
ROUND_DOWN
ROUND_UP
START_FROM_BEGINNING
STILL_TYPING
STOPPED_TYPING
STRIP_LINE
TYPING
USE_INDENT_IGNORE
WORD_REGEXP

Attributes

changing_selection[R]
desired_column[W]
language[R]
last_col[R]
last_row[R]
last_screen_col[R]
last_screen_x[R]
last_screen_y[R]
last_search_regexps[R]
left_column[R]
lines[RW]
name[R]
num_matches_found[R]
original_language[R]
read_only[RW]
selection_mode[R]
tab_size[R]
top_line[R]

Public Class Methods

new( options = {} ) click to toggle source

Set name to nil to create a buffer that is not associated with a file. @param [Hash] options @option options [String] ‘filepath’

A file path (which is expanded internally)

@option options [Boolean] ‘read_only’ (READ_WRITE)

Whether the buffer should be protected from modification

@option options [Hash] ‘cursor’

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

@option options [Hash] ‘display’

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

@see READ_WRITE @see READ_ONLY

# File lib/diakonos/buffer.rb, line 46
def initialize( options = {} )
  @name = options[ 'filepath' ]
  @modified = false
  @last_modification_check = Time.now

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

  @current_buffer_state = 0

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

  configure

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

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

Public Instance Methods

==(other) click to toggle source
# File lib/diakonos/buffer.rb, line 159
def == (other)
  return false  if other.nil?
  @name == other.name
end
[]( arg ) click to toggle source
# File lib/diakonos/buffer.rb, line 155
def [] ( arg )
  @lines[ arg ]
end
_find_down(regexps, regexp, from_row, from_col, search_area) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 67
def _find_down(regexps, regexp, from_row, from_col, search_area)
  # Check the current row first.

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

  # Check below the cursor.

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

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

  # Wrap around.

  @wrapped = true

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

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

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

  if from_row == search_area.start_row
    index_col = search_area.start_col
  else
    index_col = 0
  end
  if index = @lines[ from_row ].index( regexp, index_col )
    if index <= ( @last_finding ? @last_finding.start_col : from_col )
      finding = confirm_finding( regexps, search_area, from_row, index, Regexp.last_match )
      return finding  if finding
    end
  end
end
_find_up(regexps, regexp, search_area, from_row, from_col) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 131
def _find_up(regexps, regexp, search_area, from_row, from_col)
  # Check the current row first.

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

  # Check above the cursor.

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

  # Wrap around.

  @wrapped = true

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

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

  search_col = ( @last_finding ? @last_finding.start_col : from_col ) + 1
  if index = @lines[ from_row ].rindex( regexp )
    if index > search_col
      finding = confirm_finding( regexps, search_area, from_row, index, Regexp.last_match )
      return finding  if finding
    end
  end
end
anchor_selection( row = @last_row, col = @last_col, do_display = DO_DISPLAY ) click to toggle source
# File lib/diakonos/buffer/selection.rb, line 179
def anchor_selection( row = @last_row, col = @last_col, do_display = DO_DISPLAY )
  @mark_anchor = ( @mark_anchor || Hash.new )
  @mark_anchor[ "row" ] = row
  @mark_anchor[ "col" ] = col
  record_mark_start_and_end
  @changing_selection = true
  @auto_anchored = false
  display  if do_display
end
anchor_unanchored_selection(*args) click to toggle source
# File lib/diakonos/buffer/selection.rb, line 189
def anchor_unanchored_selection(*args)
  if @mark_anchor.nil?
    anchor_selection *args
    @changing_selection = false
  end
  @auto_anchored = true
end
carriage_return() click to toggle source
# File lib/diakonos/buffer.rb, line 385
def carriage_return
  take_snapshot
  row = @last_row
  col = @last_col
  @lines = @lines[ 0...row ] +
    [ @lines[ row ][ 0...col ] ] +
    [ @lines[ row ][ col..-1 ] ] +
    @lines[ (row+1)..-1 ]
  cursor_to( row + 1, 0 )
  if @auto_indent
    parsed_indent  undoable: false
  end
  set_modified
end
clear_matches( do_display = DONT_DISPLAY ) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 365
def clear_matches( do_display = DONT_DISPLAY )
  @text_marks[ :found ] = []
  @highlight_regexp = nil
  display  if do_display
end
clear_pair_highlight() click to toggle source
# File lib/diakonos/buffer/searching.rb, line 451
def clear_pair_highlight
  @text_marks[ :pair ] = nil
end
clear_search_area() click to toggle source
# File lib/diakonos/buffer/searching.rb, line 27
def clear_search_area
  @search_area = nil
  @text_marks.delete :search_area_pre
  @text_marks.delete :search_area_post
end
close_code() click to toggle source
# File lib/diakonos/buffer.rb, line 259
def close_code
  line = @lines[ @last_row ]
  @closers.each_value do |h|
    h[ :regexp ] =~ line
    lm = Regexp.last_match
    if lm
      str = case h[ :closer ]
      when String
        if lm[ 1 ].nil?
          h[ :closer ]
        else
          lm[ 1 ].gsub(
            Regexp.new( "(#{ Regexp.escape( lm[1] ) })" ),
            h[ :closer ]
          )
        end
      when Proc
        h[ :closer ].call( lm ).to_s
      end
      r, c = @last_row, @last_col
      paste str, !TYPING, @indent_closers
      cursor_to r, c
      if /%_/ === str
        find [/%_/], direction: :down, replacement: '', auto_choice: CHOICE_YES_AND_STOP
      end
    end
  end
end
collapse_whitespace() click to toggle source
# File lib/diakonos/buffer.rb, line 288
def collapse_whitespace
  if selection_mark
    remove_selection DONT_DISPLAY
  end

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

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

# File lib/diakonos/buffer.rb, line 419
def column_of( x )
  @left_column + x
end
column_to_x( col ) click to toggle source

Returns nil if the column is off-screen.

# File lib/diakonos/buffer.rb, line 437
def column_to_x( col )
  return nil if col.nil?
  x = col - @left_column
  x = nil if ( x < 0 ) || ( x > @left_column + Curses::cols - 1 )
  x
end
columnize( delimiter = /=>?|:|,/, num_spaces_padding = 1 ) click to toggle source
# File lib/diakonos/buffer.rb, line 306
def columnize( delimiter = /=>?|:|,/, num_spaces_padding = 1 )
  take_snapshot

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

  padding = ' ' * num_spaces_padding
  one_modified = false

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

  if one_modified
    set_modified
  end
end
comment_out() click to toggle source
# File lib/diakonos/buffer.rb, line 345
def comment_out
  take_snapshot

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

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

  if one_modified
    set_modified
  end
end
configure( language = ( $diakonos.get_language_from_shabang( @lines[ 0 ] ) || $diakonos.get_language_from_name( @name ) || LANG_TEXT ) ) click to toggle source
# File lib/diakonos/buffer.rb, line 116
def configure(
  language = (
    $diakonos.get_language_from_shabang( @lines[ 0 ] ) ||
    $diakonos.get_language_from_name( @name ) ||
    LANG_TEXT
  )
)
  reset_display
  set_language language
  @original_language = @language
end
confirm_finding( regexps, search_area, from_row, from_col, match ) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 50
def confirm_finding( regexps, search_area, from_row, from_col, match )
  found_text = match[0]
  range = ::Diakonos::Range.new(from_row, from_col, from_row, from_col + found_text.length)
  Finding.confirm(range, regexps, @lines, search_area, match)
end
context() click to toggle source
# File lib/diakonos/buffer.rb, line 576
def context
  retval = Array.new
  row = @last_row
  clevel = indentation_level( row )
  while row > 0 && clevel < 0
    row = row - 1
    clevel = indentation_level( row )
  end
  clevel = 0  if clevel < 0
  while row > 0
    row = row - 1
    line = @lines[ row ]
    if ! line.strip.empty? && ( line !~ @settings[ "lang.#{@language}.context.ignore" ] )
      level = indentation_level( row )
      if level < clevel && level > -1
        retval.unshift line
        clevel = level
        break  if clevel == 0
      end
    end
  end
  retval
end
copy_selection() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 214
def copy_selection
  selected_text
end
current_column() click to toggle source
# File lib/diakonos/buffer.rb, line 448
def current_column
  @last_col
end
current_line() click to toggle source
# File lib/diakonos/buffer.rb, line 409
def current_line
  @lines[ @last_row ]
end
current_row() click to toggle source
# File lib/diakonos/buffer.rb, line 444
def current_row
  @last_row
end
cursor_to( row, col, do_display = DONT_DISPLAY, stopped_typing = STOPPED_TYPING, adjust_row = ADJUST_ROW ) click to toggle source

Returns true iff the cursor changed positions in the buffer.

# File lib/diakonos/buffer/cursor.rb, line 8
def cursor_to( row, col, do_display = DONT_DISPLAY, stopped_typing = STOPPED_TYPING, adjust_row = ADJUST_ROW )
  old_last_row = @last_row
  old_last_col = @last_col

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

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

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

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

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

  record_mark_start_and_end

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

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

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

  ( @last_row != old_last_row || @last_col != old_last_col )
end
cursor_to_bol() click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 95
def cursor_to_bol
  row = @last_row
  case @settings[ "bol_behaviour" ]
  when BOL_ZERO
    col = 0
  when BOL_FIRST_CHAR
    col = ( ( @lines[ row ] =~ /\S/ ) || 0 )
  when BOL_ALT_ZERO
    if @last_col == 0
      col = ( @lines[ row ] =~ /\S/ )
    else
      col = 0
    end
    #when BOL_ALT_FIRST_CHAR
  else
    first_char_col = ( ( @lines[ row ] =~ /\S/ ) || 0 )
    if @last_col == first_char_col
      col = 0
    else
      col = first_char_col
    end
  end
  cursor_to( row, col, DO_DISPLAY )
end
cursor_to_bov() click to toggle source

Bottom of view

# File lib/diakonos/buffer/cursor.rb, line 150
def cursor_to_bov
  cursor_to( row_of( 0 + $diakonos.main_window_height - 1 ), @last_col, DO_DISPLAY )
end
cursor_to_eof() click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 91
def cursor_to_eof
  cursor_to( @lines.length - 1, @lines[ -1 ].length, DO_DISPLAY )
end
cursor_to_eol() click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 120
def cursor_to_eol
  y = @win_main.cury
  end_col = line_at( y ).length
  last_char_col = line_at( y ).rstrip.length
  case @settings[ 'eol_behaviour' ]
  when EOL_END
    col = end_col
  when EOL_LAST_CHAR
    col = last_char_col
  when EOL_ALT_LAST_CHAR
    if @last_col == last_char_col
      col = end_col
    else
      col = last_char_col
    end
  else
    if @last_col == end_col
      col = last_char_col
    else
      col = end_col
    end
  end
  cursor_to( @last_row, col, DO_DISPLAY )
end
cursor_to_tov() click to toggle source

Top of view

# File lib/diakonos/buffer/cursor.rb, line 146
def cursor_to_tov
  cursor_to( row_of( 0 ), @last_col, DO_DISPLAY )
end
delete() click to toggle source

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

# 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 ) click to toggle source
# 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 ) click to toggle source
# 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_line() click to toggle source
# 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 ) click to toggle source
# File lib/diakonos/buffer/selection.rb, line 268
def delete_selection( do_display = DO_DISPLAY )
  return  if @text_marks[ :selection ].nil?

  take_snapshot

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

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

  cursor_to start_row, start_col
  remove_selection DONT_DISPLAY
  set_modified do_display
end
delete_to( char ) click to toggle source
# 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 ) click to toggle source
# 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_eol() click to toggle source
# 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
display() click to toggle source
# File lib/diakonos/buffer/display.rb, line 272
def display
  @continued_format_class = nil
  @pen_down = true
  @lang_stack = []

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

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

    open_index, open_token_class, open_match_text = find_opening_match( line )

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

      break
    end

    index = index - 1
  end

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

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

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

    @win_main.addstr linestr
  end

  paint_column_markers

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

  if @language != @original_language
    set_language( @original_language )
  end
end
file_different?() click to toggle source

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

# 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?() click to toggle source

Check if the file which is being edited has been modified since the last time we checked it. @return true if file has been modified @return false if file has not been modified

# 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 = {} ) click to toggle source

@param regexps [Array] Regexps which represent 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. @return [Integer] the number of replacements made

# File lib/diakonos/buffer/searching.rb, line 176
def find( regexps, options = {} )
  return  if regexps.nil?
  regexp = regexps[ 0 ]
  return  if regexp.nil? || regexp == //

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

  if options[:starting]
    @num_matches_found = nil
  end

  num_replacements = 0

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

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

  @wrapped = false

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

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

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

    @changing_selection = false

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

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

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

  num_replacements
end
find_again( last_search_regexps, direction = @last_search_direction ) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 455
def find_again( last_search_regexps, direction = @last_search_direction )
  if @last_search_regexps.nil? || @last_search_regexps.empty?
    @last_search_regexps = last_search_regexps
  end
  if @last_search_regexps
    find @last_search_regexps, direction: direction
  end
end
find_opening_match( line, match_close = true, bos_allowed = true ) click to toggle source
# 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_inner() click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 208
def go_block_inner
  initial_level = indentation_level( @last_row )
  new_row = @lines.length
  ( @last_row...@lines.length ).each do |row|
    next  if @lines[ row ].strip.empty?
    level = indentation_level( row )
    if level > initial_level
      new_row = row
      break
    elsif level < initial_level
      new_row = @last_row
      break
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end
go_block_next() click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 225
def go_block_next
  initial_level = indentation_level( @last_row )
  new_row = @last_row
  passed = false
  ( @last_row+1...@lines.length ).each do |row|
    next  if @lines[ row ].strip.empty?
    level = indentation_level( row )
    if ! passed
      if level < initial_level
        passed = true
      end
    else
      if level == initial_level
        new_row = row
        break
      elsif level < initial_level - 1
        break
      end
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end
go_block_outer() click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 185
def go_block_outer
  initial_level = indentation_level( @last_row )
  new_row = @last_row
  passed = false
  new_level = initial_level
  ( 0...@last_row ).reverse_each do |row|
    next  if @lines[ row ].strip.empty?
    level = indentation_level( row )
    if ! passed
      passed = ( level < initial_level )
      new_level = level
    else
      if level < new_level
        new_row = ( row+1..@last_row ).find { |r|
          ! @lines[ r ].strip.empty?
        }
        break
      end
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end
go_block_previous() click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 248
def go_block_previous
  initial_level = indentation_level( @last_row )
  new_row = @last_row
  passed = false   # search for unindent
  passed2 = false  # search for reindent
  ( 0...@last_row ).reverse_each do |row|
    next  if @lines[ row ].strip.empty?
    level = indentation_level( row )
    if ! passed
      if level < initial_level
        passed = true
      end
    else
      if ! passed2
        if level >= initial_level
          new_row = row
          passed2 = true
        elsif level <= initial_level - 2
          # No previous block
          break
        end
      else
        if level < initial_level
          new_row = ( row+1..@last_row ).find { |r|
            ! @lines[ r ].strip.empty?
          }
          break
        end
      end
    end
  end
  go_to_line( new_row, @lines[ new_row ].index( /\S/ ) )
end
go_to_char( char, after = ON_CHAR ) click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 282
def go_to_char( char, after = ON_CHAR )
  r = @last_row
  i = @lines[ r ].index( char, @last_col + 1 )
  if i
    if after
      i += 1
    end
    return cursor_to r, i, DO_DISPLAY
  end

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

    i = @lines[ r ].index( char )
    if i
      if after
        i += 1
      end
      return cursor_to r, i, DO_DISPLAY
    end
  end
end
go_to_char_previous( char, after = ON_CHAR ) click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 306
def go_to_char_previous( char, after = ON_CHAR )
  r = @last_row
  search_from = @last_col - 1
  if search_from >= 0
    i = @lines[ r ].rindex( char, search_from )
    if i
      if after
        i += 1
      end
      return cursor_to r, i, DO_DISPLAY
    end
  end

  loop do
    r -= 1
    break  if r < 0

    i = @lines[ r ].rindex( char )
    if i
      if after
        i += 1
      end
      return cursor_to r, i, DO_DISPLAY
    end
  end
end
go_to_line( line = nil, column = nil, do_display = DO_DISPLAY ) click to toggle source
# File lib/diakonos/buffer/cursor.rb, line 181
def go_to_line( line = nil, column = nil, do_display = DO_DISPLAY )
  cursor_to( line || @last_row, column || 0, do_display )
end
go_to_next_bookmark() click to toggle source
# 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_match() click to toggle source
# File lib/diakonos/buffer/searching.rb, line 429
def go_to_pair_match
  row, col = pos_of_pair_match
  if row && col
    if cursor_to( row, col )
      highlight_pair
      display
    end
  end
end
go_to_previous_bookmark() click to toggle source
# 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 ) click to toggle source

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

# File lib/diakonos/buffer/searching.rb, line 532
def grep( regexp_source )
  if @name
    filename = File.basename( @name )
    filepath = @name
  else
    filename = "(unnamed buffer)"
    filepath = "(unnamed buffer #{object_id})"
  end

  ::Diakonos.grep_array(
    Regexp.new( regexp_source ),
    @lines,
    $diakonos.settings[ 'grep.context' ],
    "#{filename}:",
    filepath
  )
end
highlight_matches( regexp = @highlight_regexp ) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 334
def highlight_matches( regexp = @highlight_regexp )
  @highlight_regexp = regexp
  return  if @highlight_regexp.nil?

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

  grepped_lines = lines.grep_indices( @highlight_regexp )
  n = grepped_lines.count
  found_marks = grepped_lines.collect do |line_index, start_col, end_col|
    TextMark.new(
      ::Diakonos::Range.new(
        line_index + line_index_offset,
        start_col + ( line_index == 0 ? col_offset : 0 ),
        line_index + line_index_offset,
        end_col,
      ),
      @settings["lang.#{@language}.format.found"]
    )
  end
  @text_marks[:found] = found_marks
  @num_matches_found ||= found_marks.size
end
highlight_pair() click to toggle source
# File lib/diakonos/buffer/searching.rb, line 439
def highlight_pair
  match_row, match_col = pos_of_pair_match( @last_row, @last_col )
  if match_col.nil?
    @text_marks[ :pair ] = nil
  else
    @text_marks[ :pair ] = TextMark.new(
      ::Diakonos::Range.new(match_row, match_col, match_row, match_col + 1),
      @settings[ "lang.#{@language}.format.pair" ] || @settings[ "lang.shared.format.pair" ]
    )
  end
end
in_line( x, y ) click to toggle source

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

# File lib/diakonos/buffer.rb, line 414
def in_line( x, y )
  x + @left_column < line_at( y ).length
end
indent( row = @last_row, do_display = DO_DISPLAY ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# File lib/diakonos/buffer.rb, line 188
def insert_char( c )
  row = @last_row
  col = @last_col
  take_snapshot( TYPING )
  line = @lines[ row ]
  @lines[ row ] = line[ 0...col ] + c.chr + line[ col..-1 ]
  set_modified
end
insert_string( str ) click to toggle source
# File lib/diakonos/buffer.rb, line 197
def insert_string( str )
  row = @last_row
  col = @last_col
  take_snapshot( TYPING )
  line = @lines[ row ]
  @lines[ row ] = line[ 0...col ] + str + line[ col..-1 ]
  set_modified
end
join_lines( row = @last_row, strip = DONT_STRIP_LINE ) click to toggle source
# File lib/diakonos/buffer.rb, line 247
def join_lines( row = @last_row, strip = DONT_STRIP_LINE )
  take_snapshot( TYPING )
  next_line = @lines.delete_at( row + 1 )
  return false  if next_line.nil?

  if strip
    next_line = ' ' + next_line.strip
  end
  @lines[ row ] << next_line
  set_modified
end
join_lines_upward( row = @last_row, strip = DONT_STRIP_LINE ) click to toggle source
# File lib/diakonos/buffer.rb, line 220
def join_lines_upward( row = @last_row, strip = DONT_STRIP_LINE )
  return false  if row == 0

  take_snapshot

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

  new_x_pos  = old_line.length

  if strip
    line.strip!

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

  @lines[ row-1 ] << line

  cursor_to( row-1, new_x_pos )

  set_modified
end
length() click to toggle source
# File lib/diakonos/buffer.rb, line 164
def length
  @lines.length
end
line_at( y ) click to toggle source
# File lib/diakonos/buffer.rb, line 400
def line_at( y )
  row = @top_line + y
  if row < 0
    nil
  else
    @lines[ row ]
  end
end
modified?() click to toggle source
# File lib/diakonos/buffer.rb, line 172
def modified?
  @modified
end
nearest_basis_row_from(starting_row, next_line_check = true) click to toggle source

@param starting_row [Integer] @param next_line_check [Boolean] @return [Integer]

# 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_name() click to toggle source
# File lib/diakonos/buffer.rb, line 176
def nice_name
  @name || @settings[ "status.unnamed_str" ]
end
paint_column_markers() click to toggle source
# File lib/diakonos/buffer/display.rb, line 145
def paint_column_markers
  $diakonos.column_markers.each_value do |data|
    column = data[ :column ]
    next  if column.nil?
    next  if column > Curses::cols - @left_column || column - @left_column < 0

    num_lines_to_paint = [ $diakonos.main_window_height, @lines.size - @top_line ].min
    ( 0...num_lines_to_paint ).each do |row|
      @win_main.setpos( row, column - @left_column )
      @win_main.attrset data[ :format ]
      @win_main.addstr( @lines[ @top_line + row ][ column + @left_column ] || ' ' )
    end
  end
end
paint_marks( row ) click to toggle source
# File lib/diakonos/buffer/display.rb, line 101
def paint_marks( row )
  string = @lines[ row ][ @left_column ... @left_column + Curses::cols ]
  return  if string.nil? || string == ""
  string = string.expand_tabs( @tab_size )
  cury = @win_main.cury
  curx = @win_main.curx

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

    @win_main.attrset text_mark.formatting

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

Worker function for painting only part of a row.

# File lib/diakonos/buffer/display.rb, line 89
def paint_single_row_mark( row, text_mark, string, curx, cury )
  expanded_col = tab_expanded_column( text_mark.start_col, row )
  if expanded_col < @left_column + Curses::cols
    left = [ expanded_col - @left_column, 0 ].max
    right = tab_expanded_column( text_mark.end_col, row ) - @left_column
    if left < right
      @win_main.setpos( cury, curx + left )
      @win_main.addstr string[ left...right ]
    end
  end
end
pan_view( x = 1, do_display = DO_DISPLAY ) click to toggle source

Returns the amount the view was actually panned.

# File lib/diakonos/buffer.rb, line 459
def pan_view( x = 1, do_display = DO_DISPLAY )
  old_left_column = @left_column
  pan_view_to( @left_column + x, do_display )
  @left_column - old_left_column
end
pan_view_to( left_column, do_display = DO_DISPLAY ) click to toggle source
# File lib/diakonos/buffer.rb, line 452
def pan_view_to( left_column, do_display = DO_DISPLAY )
  @left_column = [ left_column, 0 ].max
  record_mark_start_and_end
  display  if do_display
end
paragraph_under_cursor() click to toggle source

Returns an array of lines of the current paragraph.

# File lib/diakonos/buffer.rb, line 650
def paragraph_under_cursor
  ( first, _ ), ( last, _ ) = paragraph_under_cursor_pos
  @lines[ first..last ]
end
paragraph_under_cursor_pos() click to toggle source

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

# File lib/diakonos/buffer.rb, line 657
def paragraph_under_cursor_pos
  if @lines[ @last_row ] =~ /^\s*$/
    return [
      [ @last_row, 0 ],
      [ @last_row, @lines[ @last_row ].length - 1 ]
    ]
  end

  upper_boundary = 0
  lower_boundary = @lines.size - 1

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

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

  [
    [ upper_boundary, 0 ],
    [ lower_boundary, @lines[ lower_boundary ].length - 1 ]
  ]
end
parsed_indent( opts = {} ) click to toggle source
# 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 ) click to toggle source

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

# File lib/diakonos/buffer/selection.rb, line 301
def paste( text, typing = ! TYPING, do_parsed_indent = false )
  return  if text.nil?

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

  take_snapshot typing

  delete_selection DONT_DISPLAY

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

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

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

  end

  set_modified
end
pitch_view( y = 1, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY ) click to toggle source

Returns the amount the view was actually pitched.

# File lib/diakonos/buffer.rb, line 537
def pitch_view( y = 1, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY )
  pitch_view_to( @top_line + y, do_pitch_cursor, do_display )
end
pitch_view_to( new_top_line, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY ) click to toggle source
# File lib/diakonos/buffer.rb, line 465
def pitch_view_to( new_top_line, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY )
  old_top_line = @top_line

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

  old_row = @last_row
  old_col = @last_col

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

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

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

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

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

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

  record_mark_start_and_end

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

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

  display  if do_display

  changed
end
pos_of_next( regexp, start_row, start_col ) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 371
def pos_of_next( regexp, start_row, start_col )
  row, col = start_row, start_col
  col = @lines[ row ].index( regexp, col )
  while col.nil? && row < @lines.length - 1
    row += 1
    col = @lines[ row ].index( regexp )
  end
  if col
    [ row, col, Regexp.last_match( 0 ) ]
  end
end
pos_of_pair_match( row = @last_row, col = @last_col ) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 401
def pos_of_pair_match( row = @last_row, col = @last_col )
  c = @lines[ row ][ col ]
  data = CHARACTER_PAIRS[ c ]
  return  if data.nil?
  d = data[ :partner ]
  c_ = Regexp.escape c
  d_ = Regexp.escape d
  target = /(?:#{c_}|#{d_})/

  case data[ :direction ]
  when :forward
    row, col, char = pos_of_next( target, row, col + 1 )
    while char == c  # Take care of nested pairs
      row, col = pos_of_pair_match( row, col )
      break  if col.nil?
      row, col, char = pos_of_next( target, row, col + 1 )
    end
  when :backward
    row, col, char = pos_of_prev( target, row, col - 1 )
    while char == c  # Take care of nested pairs
      row, col = pos_of_pair_match( row, col )
      break  if col.nil?
      row, col, char = pos_of_prev( target, row, col - 1 )
    end
  end
  [ row, col ]
end
pos_of_prev( regexp, start_row, start_col ) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 383
def pos_of_prev( regexp, start_row, start_col )
  row, col = start_row, start_col
  if col < 0
    row -= 1
    col = -1
  end
  return  if row < 0

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

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

print_padding_from( col ) click to toggle source
print_string( string, formatting = ( @token_formats[ @continued_format_class ] || @default_formatting ) ) click to toggle source
record_mark_start_and_end() click to toggle source

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

# File lib/diakonos/buffer/selection.rb, line 7
def record_mark_start_and_end
  if @mark_anchor.nil?
    @text_marks[ :selection ] = nil
    return
  end

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

  case @selection_mode
  when :normal
    anchor_first = true

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

    if anchor_first
      @text_marks[ :selection ] = TextMark.new( ::Diakonos::Range.new(arow, acol, crow, ccol), @selection_formatting )
    else
      @text_marks[ :selection ] = TextMark.new( ::Diakonos::Range.new(crow, ccol, arow, acol), @selection_formatting )
    end
  when :block
    if crow < arow
      if ccol < acol # Northwest
        @text_marks[ :selection ] = TextMark.new( ::Diakonos::Range.new(crow, ccol, arow, acol), @selection_formatting )
      else           # Northeast
        @text_marks[ :selection ] = TextMark.new( ::Diakonos::Range.new(crow, acol, arow, ccol), @selection_formatting )
      end
    else
      if ccol < acol  # Southwest
        @text_marks[ :selection ] = TextMark.new( ::Diakonos::Range.new(arow, ccol, crow, acol), @selection_formatting )
      else            # Southeast
        @text_marks[ :selection ] = TextMark.new( ::Diakonos::Range.new(arow, acol, crow, ccol), @selection_formatting )
      end
    end
  end
end
remove_selection( do_display = DO_DISPLAY ) click to toggle source
# File lib/diakonos/buffer/selection.rb, line 197
def remove_selection( do_display = DO_DISPLAY )
  return  if selection_mark.nil?
  @mark_anchor = nil
  record_mark_start_and_end
  @changing_selection = false
  @last_finding = nil
  display  if do_display
end
replace_all( regexp, replacement, within_search_area = false ) click to toggle source

@return [Integer] the number of replacements made

# File lib/diakonos/buffer/searching.rb, line 300
def replace_all( regexp, replacement, within_search_area = false )
  return  if( regexp.nil? || replacement.nil? )

  num_replacements = 0

  take_snapshot

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

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

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

  set_modified
  clear_matches
  display
  num_replacements
end
replace_char( c ) click to toggle source
# File lib/diakonos/buffer.rb, line 180
def replace_char( c )
  row = @last_row
  col = @last_col
  take_snapshot TYPING
  @lines[ row ][ col ] = c
  set_modified
end
reset_display() click to toggle source
# File lib/diakonos/buffer.rb, line 128
def reset_display
  @win_main = $diakonos.win_main
  @win_line_numbers = $diakonos.win_line_numbers
end
row_of( y ) click to toggle source

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

# File lib/diakonos/buffer.rb, line 424
def row_of( y )
  @top_line + y
end
row_to_y( row ) click to toggle source

Returns nil if the row is off-screen.

# File lib/diakonos/buffer.rb, line 429
def row_to_y( row )
  return nil if row.nil?
  y = row - @top_line
  y = nil if ( y < 0 ) || ( y > @top_line + $diakonos.main_window_height - 1 )
  y
end
save( filename = nil, prompt_overwrite = DONT_PROMPT_OVERWRITE ) click to toggle source
# 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 ) click to toggle source

Returns true on successful write.

# 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?() click to toggle source
# File lib/diakonos/buffer/searching.rb, line 23
def search_area?
  @search_area
end
search_area_lines() click to toggle source

@return [Array<String>]

# File lib/diakonos/buffer/searching.rb, line 57
def search_area_lines
  return []  if @search_area.nil?

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

  lines
end
seek( regexp, direction = :down ) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 464
def seek( regexp, direction = :down )
  return  if regexp.nil? || regexp == //

  found_row = nil
  found_col = nil
  found_text = nil

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

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

      # Check below the cursor.

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

    else
      # Check the current row first.

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

      # Check above the cursor.

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

  if found_text
    cursor_to( found_row, found_col )
    display
    true
  end
end
select( from_regexp, to_regexp, include_ending = true ) click to toggle source
# File lib/diakonos/buffer/selection.rb, line 146
def select( from_regexp, to_regexp, include_ending = true )
  start_row = nil

  @lines[ 0..@last_row ].reverse.each_with_index do |line,index|
    if line =~ from_regexp
      start_row = @last_row - index
      break
    end
  end
  if start_row
    end_row = nil
    @lines[ start_row..-1 ].each_with_index do |line,index|
      if line =~ to_regexp
        end_row = start_row + index
        break
      end
    end
    if end_row
      if include_ending
        end_row += 1
      end
      anchor_selection( start_row, 0, DONT_DISPLAY )
      cursor_to( end_row, 0 )
      display
    end
  end
end
select_all() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 83
def select_all
  selection_mode_normal
  anchor_selection( 0, 0, DONT_DISPLAY )
  cursor_to( @lines.length - 1, @lines[ -1 ].length, DO_DISPLAY )
end
select_current_line() click to toggle source
# 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_word() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 122
def select_word
  coords = word_under_cursor_pos( or_after: true )
  if coords.nil?
    remove_selection
  else
    cursor_to *coords[0]
    anchor_selection
    cursor_to *coords[1]
    display
  end
end
select_word_another() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 134
def select_word_another
  m = selection_mark
  if m.nil?
    select_word
  else
    row, col, _ = pos_of_next( /\w\b/, @last_row, @last_col )
    if row && col
      cursor_to row, col+1, DO_DISPLAY
    end
  end
end
select_wrapping_block() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 89
def select_wrapping_block
  block_level = indentation_level( @last_row )
  if selecting?
    block_level -= 1
  end
  if block_level <= 0
    return select_all
  end

  end_row = start_row = @last_row

  # Find block end
  ( @last_row...@lines.size ).each do |row|
    next  if @lines[ row ].strip.empty?
    if indentation_level( row ) < block_level
      end_row = row
      break
    end
  end

  # Go to block beginning
  ( 0...@last_row ).reverse_each do |row|
    next  if @lines[ row ].strip.empty?
    if indentation_level( row ) < block_level
      start_row = row + 1
      break
    end
  end

  anchor_selection( end_row, 0 )
  cursor_to( start_row, 0, DO_DISPLAY )
end
selected_lines() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 247
def selected_lines
  selection = selection_mark
  if selection
    if selection.end_col == 0
      end_row = selection.end_row - 1
    else
      end_row = selection.end_row
    end
    @lines[ selection.start_row..end_row ]
  else
    [ @lines[ @last_row ] ]
  end
end
selected_string() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 238
def selected_string
  lines = selected_text
  if lines
    lines.join( "\n" )
  else
    nil
  end
end
selected_text() click to toggle source

@return [Array<String>, nil]

# File lib/diakonos/buffer/selection.rb, line 219
def selected_text
  selection = selection_mark
  if selection.nil?
    nil
  elsif selection.start_row == selection.end_row
    [ @lines[ selection.start_row ][ selection.start_col...selection.end_col ] ]
  else
    if @selection_mode == :block
      @lines[ selection.start_row .. selection.end_row ].collect { |line|
        line[ selection.start_col ... selection.end_col ]
      }
    else
      [ @lines[ selection.start_row ][ selection.start_col..-1 ] ] +
        ( @lines[ (selection.start_row + 1) .. (selection.end_row - 1) ] || [] ) +
        [ @lines[ selection.end_row ][ 0...selection.end_col ] ]
    end
  end
end
selecting?() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 58
def selecting?
  !! selection_mark
end
selection_mark() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 54
def selection_mark
  @text_marks[:selection]
end
selection_mode_block() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 261
def selection_mode_block
  @selection_mode = :block
end
selection_mode_normal() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 264
def selection_mode_normal
  @selection_mode = :normal
end
set_indent( row, level, opts = {} ) click to toggle source
# 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 ) click to toggle source
# File lib/diakonos/buffer.rb, line 133
def set_language( language )
  @settings = $diakonos.settings
  @language = language
  @surround_pairs = $diakonos.surround_pairs[ @language ]
  @token_regexps = $diakonos.token_regexps[ @language ]
  @close_token_regexps = $diakonos.close_token_regexps[ @language ]
  @token_formats = $diakonos.token_formats[ @language ]
  @indenters = $diakonos.indenters[ @language ]
  @indenters_next_line = $diakonos.indenters_next_line[ @language ]
  @unindenters = $diakonos.unindenters[ @language ]
  @preventers = @settings[ "lang.#{@language}.indent.preventers" ]
  @closers = $diakonos.closers[ @language ] || Hash.new
  @auto_indent = @settings[ "lang.#{@language}.indent.auto" ]
  @indent_size = ( @settings[ "lang.#{@language}.indent.size" ] || 4 )
  @indent_roundup = @settings[ "lang.#{@language}.indent.roundup" ].nil? ? true : @settings[ "lang.#{@language}.indent.roundup" ]
  @indent_closers = @settings[ "lang.#{@language}.indent.closers" ].nil? ? false : @settings[ "lang.#{@language}.indent.closers" ]
  @default_formatting = ( @settings[ "lang.#{@language}.format.default" ] || Curses::A_NORMAL )
  @selection_formatting = ( @settings[ "lang.#{@language}.format.selection" ] || Curses::A_REVERSE )
  @indent_ignore_charset = ( @settings[ "lang.#{@language}.indent.ignore.charset" ] || "" )
  @tab_size = ( @settings[ "lang.#{@language}.tabsize" ] || DEFAULT_TAB_SIZE )
end
set_modified( do_display = DO_DISPLAY, use_md5 = DONT_USE_MD5 ) click to toggle source
# File lib/diakonos/buffer/file.rb, line 142
def set_modified( do_display = DO_DISPLAY, use_md5 = DONT_USE_MD5 )
  if @read_only
    $diakonos.set_iline "Warning: Modifying a read-only file."
  end

  @modified = use_md5 ? file_different? : true
  clear_matches
  if do_display
    $diakonos.update_status_line
    display
  end
end
set_search_area(mark) click to toggle source
# File lib/diakonos/buffer/searching.rb, line 33
def set_search_area(mark)
  if mark.nil?
    raise 'Call Diakonos::Buffer#clear_search_area instead'
    return
  end

  @search_area = mark
  @text_marks[ :search_area_pre ] = TextMark.new(
    ::Diakonos::Range.new(0, 0, mark.start_row, mark.start_col),
    @settings[ 'view.non_search_area.format' ]
  )
  @text_marks[ :search_area_post ] = TextMark.new(
    ::Diakonos::Range.new(mark.end_row, mark.end_col, @lines.length - 1, @lines[ -1 ].length),
    @settings[ 'view.non_search_area.format' ]
  )
end
set_selection( start_row, start_col, end_row, end_col ) click to toggle source
# File lib/diakonos/buffer/selection.rb, line 174
def set_selection( start_row, start_col, end_row, end_col )
  @text_marks[ :selection ] = TextMark.new( ::Diakonos::Range.new(start_row, start_col, end_row, end_col), @selection_formatting )
  @changing_selection = false
end
set_selection_current_line() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 75
def set_selection_current_line
  @text_marks[ :selection ] = TextMark.new(
    ::Diakonos::Range.new(@last_row, 0, @last_row, @lines[ @last_row ].size),
    @selection_formatting
  )
  @lines[ @last_row ]
end
set_type( type ) click to toggle source
# File lib/diakonos/buffer.rb, line 600
def set_type( type )
  return false  if type.nil?
  configure( type )
  display
  true
end
show_character( row, col ) click to toggle source

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

# File lib/diakonos/buffer/cursor.rb, line 156
def show_character( row, col )
  old_top_line = @top_line
  old_left_column = @left_column

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

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

  @top_line != old_top_line || @left_column != old_left_column
end
surround( text, parenthesis ) click to toggle source
# File lib/diakonos/buffer.rb, line 206
def surround( text, parenthesis )
  pattern, pair = @surround_pairs.select { |r, p| parenthesis =~ r }.to_a[ 0 ]

  if pair.nil?
    $diakonos.set_iline "No matching parentheses pair found."
    nil
  else
    pair.map! do |paren|
      parenthesis.gsub( pattern, paren )
    end
    pair[ 0 ] + text.join( "\n" ) + pair[ 1 ]
  end
end
tab_expanded_column( col, row ) click to toggle source
# 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 ) click to toggle source
# 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_a() click to toggle source
# File lib/diakonos/buffer.rb, line 168
def to_a
  @lines.dup
end
toggle_bookmark() click to toggle source
# 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_selection() click to toggle source
# File lib/diakonos/buffer/selection.rb, line 206
def toggle_selection
  if @changing_selection
    remove_selection
  else
    anchor_selection
  end
end
truncate_off_screen( string, write_cursor_col ) click to toggle source

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

# File lib/diakonos/buffer/display.rb, line 64
def truncate_off_screen( string, write_cursor_col )
  retval = string

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

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

  retval == "" ? nil : retval
end
uncomment() click to toggle source
# File lib/diakonos/buffer.rb, line 366
def uncomment
  take_snapshot
  comment_string = Regexp.escape( @settings[ "lang.#{@language}.comment_string" ].to_s )
  comment_close_string = Regexp.escape( @settings[ "lang.#{@language}.comment_close_string" ].to_s )
  one_modified = false
  self.selected_lines.each do |line|
    old_line = line.dup
    comment_regexp = /^(\s*)#{comment_string}/
    line.gsub!( comment_regexp, "\\1" )
    if line !~ comment_regexp
      line.gsub!( /#{comment_close_string}$/, '' )
    end
    one_modified ||= ( line != old_line )
  end
  if one_modified
    set_modified
  end
end
undo() click to toggle source
# 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 ) click to toggle source
# 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
unundo() click to toggle source

Since redo is a Ruby keyword…

# 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_cursor() click to toggle source
# File lib/diakonos/buffer.rb, line 634
def word_before_cursor
  word = nil

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

  word
end
word_under_cursor() click to toggle source
# File lib/diakonos/buffer.rb, line 607
def word_under_cursor
  pos = word_under_cursor_pos
  return  if pos.nil?

  col1 = pos[ 0 ][ 1 ]
  col2 = pos[ 1 ][ 1 ]
  @lines[ @last_row ][ col1...col2 ]
end
word_under_cursor_pos( options = {} ) click to toggle source
# File lib/diakonos/buffer.rb, line 616
def word_under_cursor_pos( options = {} )
  or_after = options[:or_after]
  @lines[ @last_row ].scan( WORD_REGEXP ) do |match_text|
    last_match = Regexp.last_match
    if (
      last_match.begin( 0 ) <= @last_col && @last_col < last_match.end( 0 ) ||
      or_after && last_match.begin(0) > @last_col
    )
      return [
        [ @last_row, last_match.begin( 0 ) ],
        [ @last_row, last_match.end( 0 ) ],
      ]
    end
  end

  nil
end
words( filter_regexp = nil ) click to toggle source

TODO paragraph_before_cursor(_pos)?

# File lib/diakonos/buffer.rb, line 691
def words( filter_regexp = nil )
  w = @lines.join( ' ' ).scan( WORD_REGEXP )
  filter_regexp ? w.grep( filter_regexp ) : w
end
wrap_paragraph() click to toggle source
# File lib/diakonos/buffer.rb, line 541
def wrap_paragraph
  start_row = end_row = cursor_row = @last_row
  cursor_col = @last_col
  until start_row == 0 || @lines[ start_row - 1 ].strip == ''
    start_row -= 1
  end
  until end_row == @lines.size || @lines[ end_row ].strip == ''
    end_row += 1
  end

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

Private Instance Methods

find_closing_match(line_segment, regexp, bos_allowed = true) click to toggle source
# File lib/diakonos/buffer/display.rb, line 38
        def find_closing_match(line_segment, regexp, bos_allowed = true)
  close_match_text = nil
  close_index = nil

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